]> git.sesse.net Git - rdpsrv/blobdiff - Xserver/programs/Xserver/mi/miarc.c
Import X server from vnc-3.3.7.
[rdpsrv] / Xserver / programs / Xserver / mi / miarc.c
diff --git a/Xserver/programs/Xserver/mi/miarc.c b/Xserver/programs/Xserver/mi/miarc.c
new file mode 100644 (file)
index 0000000..a1f426c
--- /dev/null
@@ -0,0 +1,3677 @@
+/***********************************************************
+
+Copyright (c) 1987  X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+
+
+Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
+
+                        All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its 
+documentation for any purpose and without fee is hereby granted, 
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in 
+supporting documentation, and that the name of Digital not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.  
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+/* $XConsortium: miarc.c /main/90 1996/08/01 19:25:10 dpw $ */
+/* Author: Keith Packard and Bob Scheifler */
+/* Warning: this code is toxic, do not dally very long here. */
+
+/* $XFree86: xc/programs/Xserver/mi/miarc.c,v 3.4.2.1 1997/07/13 14:45:05 dawes Exp $ */
+
+#ifdef _XOPEN_SOURCE
+#include <math.h>
+#else
+#define _XOPEN_SOURCE  /* to get prototype for hypot on some systems */
+#include <math.h>
+#undef _XOPEN_SOURCE
+#endif
+#include "X.h"
+#include "Xprotostr.h"
+#include "misc.h"
+#include "gcstruct.h"
+#include "scrnintstr.h"
+#include "pixmapstr.h"
+#include "windowstr.h"
+#include "mifpoly.h"
+#include "mi.h"
+#include "mifillarc.h"
+#include "Xfuncproto.h"
+
+static double miDsin(), miDcos(), miDasin(), miDatan2();
+double cbrt(
+#if NeedFunctionPrototypes
+            double
+#endif
+);
+
+#ifdef ICEILTEMPDECL
+ICEILTEMPDECL
+#endif
+
+/*
+ * some interesting sematic interpretation of the protocol:
+ *
+ * Self intersecting arcs (i.e. those spanning 360 degrees) 
+ *  never join with other arcs, and are drawn without caps
+ *  (unless on/off dashed, in which case each dash segment
+ *  is capped, except when the last segment meets the
+ *  first segment, when no caps are drawn)
+ *
+ * double dash arcs are drawn in two parts, first the
+ *  odd dashes (drawn in background) then the even dashes
+ *  (drawn in foreground).  This means that overlapping
+ *  sections of foreground/background are drawn twice,
+ *  first in background then in foreground.  The double-draw
+ *  occurs even when the function uses the destination values
+ *  (e.g. xor mode).  This is the same way the wide-line
+ *  code works and should be "fixed".
+ *
+ */
+
+#undef max
+#undef min
+
+#if defined (__GNUC__) && defined (__STDC__) && !defined (__STRICT_ANSI__)
+#define USE_INLINE
+#endif
+
+#ifdef USE_INLINE
+inline static const int max (const int x, const int y)
+{
+       return x>y? x:y;
+}
+
+inline static const int min (const int x, const int y)
+{
+       return x<y? x:y;
+}
+
+#else
+
+static int
+max (x, y)
+{
+       return x>y? x:y;
+}
+
+static int
+min (x, y)
+{
+       return x<y? x:y;
+}
+
+#endif
+
+struct bound {
+       double  min, max;
+};
+
+struct ibound {
+       int     min, max;
+};
+
+#define boundedLe(value, bounds)\
+       ((bounds).min <= (value) && (value) <= (bounds).max)
+
+struct line {
+       double  m, b;
+       int     valid;
+};
+
+#define intersectLine(y,line) (line.m * (y) + line.b)
+
+/*
+ * these are all y value bounds
+ */
+
+struct arc_bound {
+       struct bound    ellipse;
+       struct bound    inner;
+       struct bound    outer;
+       struct bound    right;
+       struct bound    left;
+       struct ibound   inneri;
+       struct ibound   outeri;
+};
+
+struct accelerators {
+       double          tail_y;
+       double          h2;
+       double          w2;
+       double          h4;
+       double          w4;
+       double          h2mw2;
+       double          h2l;
+       double          w2l;
+       double          fromIntX;
+       double          fromIntY;
+       struct line     left, right;
+       int             yorgu;
+       int             yorgl;
+       int             xorg;
+};
+
+struct arc_def {
+       double  w, h, l;
+       double  a0, a1;
+};
+
+# define todeg(xAngle) (((double) (xAngle)) / 64.0)
+
+# define RIGHT_END     0
+# define LEFT_END      1
+
+typedef struct _miArcJoin {
+       int     arcIndex0, arcIndex1;
+       int     phase0, phase1;
+       int     end0, end1;
+} miArcJoinRec, *miArcJoinPtr;
+
+typedef struct _miArcCap {
+       int             arcIndex;
+       int             end;            
+} miArcCapRec, *miArcCapPtr;
+
+typedef struct _miArcFace {
+       SppPointRec     clock;
+       SppPointRec     center;
+       SppPointRec     counterClock;
+} miArcFaceRec, *miArcFacePtr;
+
+typedef struct _miArcData {
+       xArc            arc;
+       int             render;         /* non-zero means render after drawing */
+       int             join;           /* related join */
+       int             cap;            /* related cap */
+       int             selfJoin;       /* final dash meets first dash */
+       miArcFaceRec    bounds[2];
+       double          x0, y0, x1, y1;
+} miArcDataRec, *miArcDataPtr;
+
+/*
+ * This is an entire sequence of arcs, computed and categorized according
+ * to operation.  miDashArcs generates either one or two of these.
+ */
+
+typedef struct _miPolyArc {
+       int             narcs;
+       miArcDataPtr    arcs;
+       int             ncaps;
+       miArcCapPtr     caps;
+       int             njoins;
+       miArcJoinPtr    joins;
+} miPolyArcRec, *miPolyArcPtr;
+
+#define GCValsFunction         0
+#define GCValsForeground       1
+#define GCValsBackground       2
+#define GCValsLineWidth        3
+#define GCValsCapStyle                 4
+#define GCValsJoinStyle                5
+#define GCValsMask             (GCFunction | GCForeground | GCBackground | \
+                                GCLineWidth | GCCapStyle | GCJoinStyle)
+static CARD32 gcvals[6];
+
+static void fillSpans(), newFinalSpan();
+static void drawArc(), drawQuadrant(), drawZeroArc();
+static void miArcJoin(), miArcCap(), miRoundCap(), miFreeArcs();
+static int computeAngleFromPath();
+static miPolyArcPtr miComputeArcs ();
+static int miGetArcPts();
+
+# define CUBED_ROOT_2  1.2599210498948732038115849718451499938964
+# define CUBED_ROOT_4  1.5874010519681993173435330390930175781250
+
+/*
+ * draw one segment of the arc using the arc spans generation routines
+ */
+
+static void
+miArcSegment(pDraw, pGC, tarc, right, left)
+    DrawablePtr   pDraw;
+    GCPtr         pGC;
+    xArc          tarc;
+    miArcFacePtr       right, left;
+{
+    int l = pGC->lineWidth;
+    int a0, a1, startAngle, endAngle;
+    miArcFacePtr       temp;
+
+    if (!l)
+       l = 1;
+
+    if (tarc.width == 0 || tarc.height == 0) {
+       drawZeroArc (pDraw, pGC, &tarc, l, left, right);
+       return;
+    }
+
+    if (pGC->miTranslate) {
+       tarc.x += pDraw->x;
+       tarc.y += pDraw->y;
+    }
+
+    a0 = tarc.angle1;
+    a1 = tarc.angle2;
+    if (a1 > FULLCIRCLE)
+       a1 = FULLCIRCLE;
+    else if (a1 < -FULLCIRCLE)
+       a1 = -FULLCIRCLE;
+    if (a1 < 0) {
+       startAngle = a0 + a1;
+       endAngle = a0;
+       temp = right;
+       right = left;
+       left = temp;
+    } else {
+       startAngle = a0;
+       endAngle = a0 + a1;
+    }
+    /*
+     * bounds check the two angles
+     */
+    if (startAngle < 0)
+       startAngle = FULLCIRCLE - (-startAngle) % FULLCIRCLE;
+    if (startAngle >= FULLCIRCLE)
+       startAngle = startAngle % FULLCIRCLE;
+    if (endAngle < 0)
+       endAngle = FULLCIRCLE - (-endAngle) % FULLCIRCLE;
+    if (endAngle > FULLCIRCLE)
+       endAngle = (endAngle-1) % FULLCIRCLE + 1;
+    if ((startAngle == endAngle) && a1) {
+       startAngle = 0;
+       endAngle = FULLCIRCLE;
+    }
+
+    drawArc (&tarc, l, startAngle, endAngle, right, left);
+}
+
+/*
+
+Three equations combine to describe the boundaries of the arc
+
+x^2/w^2 + y^2/h^2 = 1                  ellipse itself
+(X-x)^2 + (Y-y)^2 = r^2                        circle at (x, y) on the ellipse
+(Y-y) = (X-x)*w^2*y/(h^2*x)            normal at (x, y) on the ellipse
+
+These lead to a quartic relating Y and y
+
+y^4 - (2Y)y^3 + (Y^2 + (h^4 - w^2*r^2)/(w^2 - h^2))y^2
+    - (2Y*h^4/(w^2 - h^2))y + (Y^2*h^4)/(w^2 - h^2) = 0
+
+The reducible cubic obtained from this quartic is
+
+z^3 - (3N)z^2 - 2V = 0
+
+where
+
+N = (Y^2 + (h^4 - w^2*r^2/(w^2 - h^2)))/6
+V = w^2*r^2*Y^2*h^4/(4 *(w^2 - h^2)^2)
+
+Let
+
+t = z - N
+p = -N^2
+q = -N^3 - V
+
+Then we get
+
+t^3 + 3pt + 2q = 0
+
+The discriminant of this cubic is
+
+D = q^2 + p^3
+
+When D > 0, a real root is obtained as
+
+z = N + cbrt(-q+sqrt(D)) + cbrt(-q-sqrt(D))
+
+When D < 0, a real root is obtained as
+
+z = N - 2m*cos(acos(-q/m^3)/3)
+
+where
+
+m = sqrt(|p|) * sign(q)
+
+Given a real root Z of the cubic, the roots of the quartic are the roots
+of the two quadratics
+
+y^2 + ((b+A)/2)y + (Z + (bZ - d)/A) = 0
+
+where 
+
+A = +/- sqrt(8Z + b^2 - 4c)
+b, c, d are the cubic, quadratic, and linear coefficients of the quartic
+
+Some experimentation is then required to determine which solutions
+correspond to the inner and outer boundaries.
+
+*/
+
+typedef struct {
+    short lx, lw, rx, rw;
+} miArcSpan;
+
+typedef struct {
+    miArcSpan *spans;
+    int count1, count2, k;
+    char top, bot, hole;
+} miArcSpanData;
+
+typedef struct {
+    unsigned long lrustamp;
+    unsigned short lw;
+    unsigned short width, height;
+    miArcSpanData *spdata;
+} arcCacheRec;
+
+#define CACHESIZE 25
+
+static arcCacheRec arcCache[CACHESIZE];
+static unsigned long lrustamp;
+static arcCacheRec *lastCacheHit = &arcCache[0];
+static RESTYPE cacheType;
+
+/*
+ * External so it can be called when low on memory.
+ * Call with a zero ID in that case.
+ */
+/*ARGSUSED*/
+int
+miFreeArcCache (data, id)
+    pointer        data;
+    XID                    id;
+{
+    int k;
+    arcCacheRec *cent;
+
+    if (id)
+       cacheType = 0;
+
+    for (k = CACHESIZE, cent = &arcCache[0]; --k >= 0; cent++)
+    {
+       if (cent->spdata)
+       {
+           cent->lrustamp = 0;
+           cent->lw = 0;
+           xfree(cent->spdata);
+           cent->spdata = NULL;
+       }
+    }
+    lrustamp = 0;
+    return Success;
+}
+
+static void
+miComputeCircleSpans(lw, parc, spdata)
+    int lw;
+    xArc *parc;
+    miArcSpanData *spdata;
+{
+    register miArcSpan *span;
+    int doinner;
+    register int x, y, e;
+    int xk, yk, xm, ym, dx, dy;
+    register int slw, inslw;
+    int inx, iny, ine;
+    int inxk, inyk, inxm, inym;
+
+    doinner = -lw;
+    slw = parc->width - doinner;
+    y = parc->height >> 1;
+    dy = parc->height & 1;
+    dx = 1 - dy;
+    MIWIDEARCSETUP(x, y, dy, slw, e, xk, xm, yk, ym);
+    inslw = parc->width + doinner;
+    if (inslw > 0)
+    {
+       spdata->hole = spdata->top;
+       MIWIDEARCSETUP(inx, iny, dy, inslw, ine, inxk, inxm, inyk, inym);
+    }
+    else
+    {
+       spdata->hole = FALSE;
+       doinner = -y;
+    }
+    spdata->count1 = -doinner - spdata->top;
+    spdata->count2 = y + doinner;
+    span = spdata->spans;
+    while (y)
+    {
+       MIFILLARCSTEP(slw);
+       span->lx = dy - x;
+       if (++doinner <= 0)
+       {
+           span->lw = slw;
+           span->rx = 0;
+           span->rw = span->lx + slw;
+       }
+       else
+       {
+           MIFILLINARCSTEP(inslw);
+           span->lw = x - inx;
+           span->rx = dy - inx + inslw;
+           span->rw = inx - x + slw - inslw;
+       }
+       span++;
+    }
+    if (spdata->bot)
+    {
+       if (spdata->count2)
+           spdata->count2--;
+       else
+       {
+           if (lw > (int)parc->height)
+               span[-1].rx = span[-1].rw = -((lw - (int)parc->height) >> 1);
+           else
+               span[-1].rw = 0;
+           spdata->count1--;
+       }
+    }
+}
+
+static void
+miComputeEllipseSpans(lw, parc, spdata)
+    int lw;
+    xArc *parc;
+    miArcSpanData *spdata;
+{
+    register miArcSpan *span;
+    double w, h, r, xorg;
+    double Hs, Hf, WH, K, Vk, Nk, Fk, Vr, N, Nc, Z, rs;
+    double A, T, b, d, x, y, t, inx, outx, hepp, hepm;
+    int flip, solution;
+
+    w = (double)parc->width / 2.0;
+    h = (double)parc->height / 2.0;
+    r = lw / 2.0;
+    rs = r * r;
+    Hs = h * h;
+    WH = w * w - Hs;
+    Nk = w * r;
+    Vk = (Nk * Hs) / (WH + WH);
+    Hf = Hs * Hs;
+    Nk = (Hf - Nk * Nk) / WH;
+    Fk = Hf / WH;
+    hepp = h + EPSILON;
+    hepm = h - EPSILON;
+    K = h + ((lw - 1) >> 1);
+    span = spdata->spans;
+    if (parc->width & 1)
+       xorg = .5;
+    else
+       xorg = 0.0;
+    if (spdata->top)
+    {
+       span->lx = 0;
+       span->lw = 1;
+       span++;
+    }
+    spdata->count1 = 0;
+    spdata->count2 = 0;
+    spdata->hole = (spdata->top &&
+                (int)parc->height * lw <= (int)(parc->width * parc->width) &&
+                   lw < (int)parc->height);
+    for (; K > 0.0; K -= 1.0)
+    {
+       N = (K * K + Nk) / 6.0;
+       Nc = N * N * N;
+       Vr = Vk * K;
+       t = Nc + Vr * Vr;
+       d = Nc + t;
+       if (d < 0.0) {
+           d = Nc;
+           b = N;
+           if ( (b < 0.0) == (t < 0.0) )
+           {
+               b = -b;
+               d = -d;
+           }
+           Z = N - 2.0 * b * cos(acos(-t / d) / 3.0);
+           if ( (Z < 0.0) == (Vr < 0.0) )
+               flip = 2;
+           else
+               flip = 1;
+       }
+       else
+       {
+           d = Vr * sqrt(d);
+           Z = N + cbrt(t + d) + cbrt(t - d);
+           flip = 0;
+       }
+       A = sqrt((Z + Z) - Nk);
+       T = (Fk - Z) * K / A;
+       inx = 0.0;
+       solution = FALSE;
+       b = -A + K;
+       d = b * b - 4 * (Z + T);
+       if (d >= 0)
+       {
+           d = sqrt(d);
+           y = (b + d) / 2;
+           if ((y >= 0.0) && (y < hepp))
+           {
+               solution = TRUE;
+               if (y > hepm)
+                   y = h;
+               t = y / h;
+               x = w * sqrt(1 - (t * t));
+               t = K - y;
+               if (rs - (t * t) >= 0)
+                  t = sqrt(rs - (t * t));
+               else
+                  t = 0;
+               if (flip == 2)
+                   inx = x - t;
+               else
+                   outx = x + t;
+           }
+       }
+       b = A + K;
+       d = b * b - 4 * (Z - T);
+       /* Because of the large magnitudes involved, we lose enough precision
+        * that sometimes we end up with a negative value near the axis, when
+        * it should be positive.  This is a workaround.
+        */
+       if (d < 0 && !solution)
+           d = 0.0;
+       if (d >= 0) {
+           d = sqrt(d);
+           y = (b + d) / 2;
+           if (y < hepp)
+           {
+               if (y > hepm)
+                   y = h;
+               t = y / h;
+               x = w * sqrt(1 - (t * t));
+               t = K - y;
+               if (rs - (t * t) >= 0)
+                  inx = x - sqrt(rs - (t * t));
+               else
+                  inx = x;
+           }
+           y = (b - d) / 2;
+           if (y >= 0.0)
+           {
+               if (y > hepm)
+                   y = h;
+               t = y / h;
+               x = w * sqrt(1 - (t * t));
+               t = K - y;
+               if (rs - (t * t) >= 0)
+                  t = sqrt(rs - (t * t));
+               else 
+                  t = 0;
+               if (flip == 1)
+                   inx = x - t;
+               else
+                   outx = x + t;
+           }
+       }
+       span->lx = ICEIL(xorg - outx);
+       if (inx <= 0.0)
+       {
+           spdata->count1++;
+           span->lw = ICEIL(xorg + outx) - span->lx;
+           span->rx = ICEIL(xorg + inx);
+           span->rw = -ICEIL(xorg - inx);
+       }
+       else
+       {
+           spdata->count2++;
+           span->lw = ICEIL(xorg - inx) - span->lx;
+           span->rx = ICEIL(xorg + inx);
+           span->rw = ICEIL(xorg + outx) - span->rx;
+       }
+       span++;
+    }
+    if (spdata->bot)
+    {
+       outx = w + r;
+       if (r >= h && r <= w)
+           inx = 0.0;
+       else if (Nk < 0.0 && -Nk < Hs)
+       {
+           inx = w * sqrt(1 + Nk / Hs) - sqrt(rs + Nk);
+           if (inx > w - r)
+               inx = w - r;
+       }
+       else
+           inx = w - r;
+       span->lx = ICEIL(xorg - outx);
+       if (inx <= 0.0)
+       {
+           span->lw = ICEIL(xorg + outx) - span->lx;
+           span->rx = ICEIL(xorg + inx);
+           span->rw = -ICEIL(xorg - inx);
+       }
+       else
+       {
+           span->lw = ICEIL(xorg - inx) - span->lx;
+           span->rx = ICEIL(xorg + inx);
+           span->rw = ICEIL(xorg + outx) - span->rx;
+       }
+    }
+    if (spdata->hole)
+    {
+       span = &spdata->spans[spdata->count1];
+       span->lw = -span->lx;
+       span->rx = 1;
+       span->rw = span->lw;
+       spdata->count1--;
+       spdata->count2++;
+    }
+}
+
+static double
+tailX(K, def, bounds, acc)
+    double K;
+    struct arc_def *def;
+    struct arc_bound *bounds;
+    struct accelerators *acc;
+{
+    double w, h, r;
+    double Hs, Hf, WH, Vk, Nk, Fk, Vr, N, Nc, Z, rs;
+    double A, T, b, d, x, y, t, hepp, hepm;
+    int flip, solution;
+    double xs[2];
+    double *xp;
+
+    w = def->w;
+    h = def->h;
+    r = def->l;
+    rs = r * r;
+    Hs = acc->h2;
+    WH = -acc->h2mw2;
+    Nk = def->w * r;
+    Vk = (Nk * Hs) / (WH + WH);
+    Hf = acc->h4;
+    Nk = (Hf - Nk * Nk) / WH;
+    if (K == 0.0) {
+       if (Nk < 0.0 && -Nk < Hs) {
+           xs[0] = w * sqrt(1 + Nk / Hs) - sqrt(rs + Nk);
+           xs[1] = w - r;
+           if (acc->left.valid && boundedLe(K, bounds->left) &&
+               !boundedLe(K, bounds->outer) && xs[0] >= 0.0 && xs[1] >= 0.0)
+               return xs[1];
+           if (acc->right.valid && boundedLe(K, bounds->right) &&
+               !boundedLe(K, bounds->inner) && xs[0] <= 0.0 && xs[1] <= 0.0)
+               return xs[1];
+           return xs[0];
+       }
+       return w - r;
+    }
+    Fk = Hf / WH;
+    hepp = h + EPSILON;
+    hepm = h - EPSILON;
+    N = (K * K + Nk) / 6.0;
+    Nc = N * N * N;
+    Vr = Vk * K;
+    xp = xs;
+    xs[0] = 0.0;
+    t = Nc + Vr * Vr;
+    d = Nc + t;
+    if (d < 0.0) {
+       d = Nc;
+       b = N;
+       if ( (b < 0.0) == (t < 0.0) )
+       {
+           b = -b;
+           d = -d;
+       }
+       Z = N - 2.0 * b * cos(acos(-t / d) / 3.0);
+       if ( (Z < 0.0) == (Vr < 0.0) )
+           flip = 2;
+       else
+           flip = 1;
+    }
+    else
+    {
+       d = Vr * sqrt(d);
+       Z = N + cbrt(t + d) + cbrt(t - d);
+       flip = 0;
+    }
+    A = sqrt((Z + Z) - Nk);
+    T = (Fk - Z) * K / A;
+    solution = FALSE;
+    b = -A + K;
+    d = b * b - 4 * (Z + T);
+    if (d >= 0 && flip == 2)
+    {
+       d = sqrt(d);
+       y = (b + d) / 2;
+       if ((y >= 0.0) && (y < hepp))
+       {
+           solution = TRUE;
+           if (y > hepm)
+               y = h;
+           t = y / h;
+           x = w * sqrt(1 - (t * t));
+           t = K - y;
+           if (rs - (t * t) >= 0)
+              t = sqrt(rs - (t * t));
+           else
+              t = 0;
+           *xp++ = x - t;
+       }
+    }
+    b = A + K;
+    d = b * b - 4 * (Z - T);
+    /* Because of the large magnitudes involved, we lose enough precision
+     * that sometimes we end up with a negative value near the axis, when
+     * it should be positive.  This is a workaround.
+     */
+    if (d < 0 && !solution)
+       d = 0.0;
+    if (d >= 0) {
+       d = sqrt(d);
+       y = (b + d) / 2;
+       if (y < hepp)
+       {
+           if (y > hepm)
+               y = h;
+           t = y / h;
+           x = w * sqrt(1 - (t * t));
+           t = K - y;
+           if (rs - (t * t) >= 0)
+              *xp++ = x - sqrt(rs - (t * t));
+           else
+              *xp++ = x;
+       }
+       y = (b - d) / 2;
+       if (y >= 0.0 && flip == 1)
+       {
+           if (y > hepm)
+               y = h;
+           t = y / h;
+           x = w * sqrt(1 - (t * t));
+           t = K - y;
+           if (rs - (t * t) >= 0)
+              t = sqrt(rs - (t * t));
+           else
+              t = 0;
+           *xp++ = x - t;
+       }
+    }
+    if (xp > &xs[1]) {
+       if (acc->left.valid && boundedLe(K, bounds->left) &&
+           !boundedLe(K, bounds->outer) && xs[0] >= 0.0 && xs[1] >= 0.0)
+           return xs[1];
+       if (acc->right.valid && boundedLe(K, bounds->right) &&
+           !boundedLe(K, bounds->inner) && xs[0] <= 0.0 && xs[1] <= 0.0)
+           return xs[1];
+    }
+    return xs[0];
+}
+
+static miArcSpanData *
+miComputeWideEllipse(lw, parc, mustFree)
+    int                   lw;
+    register xArc *parc;
+    Bool         *mustFree;
+{
+    register miArcSpanData *spdata;
+    register arcCacheRec *cent, *lruent;
+    register int k;
+    arcCacheRec fakeent;
+
+    if (!lw)
+       lw = 1;
+    if (parc->height <= 1500)
+    {
+       *mustFree = FALSE;
+       cent = lastCacheHit;
+       if (cent->lw == lw &&
+           cent->width == parc->width && cent->height == parc->height)
+       {
+           cent->lrustamp = ++lrustamp;
+           return cent->spdata;
+       }
+       lruent = &arcCache[0];
+       for (k = CACHESIZE, cent = lruent; --k >= 0; cent++)
+       {
+           if (cent->lw == lw &&
+               cent->width == parc->width && cent->height == parc->height)
+           {
+               cent->lrustamp = ++lrustamp;
+               lastCacheHit = cent;
+               return cent->spdata;
+           }
+           if (cent->lrustamp < lruent->lrustamp)
+               lruent = cent;
+       }
+       if (!cacheType)
+       {
+           cacheType = CreateNewResourceType(miFreeArcCache);
+           (void) AddResource(FakeClientID(0), cacheType, NULL);
+       }
+    } else {
+       lruent = &fakeent;
+       lruent->spdata = NULL;
+       *mustFree = TRUE;
+    }
+    k = (parc->height >> 1) + ((lw - 1) >> 1);
+    spdata = lruent->spdata;
+    if (!spdata || spdata->k != k)
+    {
+       if (spdata)
+           xfree(spdata);
+       spdata = (miArcSpanData *)xalloc(sizeof(miArcSpanData) +
+                                        sizeof(miArcSpan) * (k + 2));
+       lruent->spdata = spdata;
+       if (!spdata)
+       {
+           lruent->lrustamp = 0;
+           lruent->lw = 0;
+           return spdata;
+       }
+       spdata->spans = (miArcSpan *)(spdata + 1);
+       spdata->k = k;
+    }
+    spdata->top = !(lw & 1) && !(parc->width & 1);
+    spdata->bot = !(parc->height & 1);
+    lruent->lrustamp = ++lrustamp;
+    lruent->lw = lw;
+    lruent->width = parc->width;
+    lruent->height = parc->height;
+    if (lruent != &fakeent)
+       lastCacheHit = lruent;
+    if (parc->width == parc->height)
+       miComputeCircleSpans(lw, parc, spdata);
+    else
+       miComputeEllipseSpans(lw, parc, spdata);
+    return spdata;
+}
+
+static void
+miFillWideEllipse(pDraw, pGC, parc)
+    DrawablePtr        pDraw;
+    GCPtr      pGC;
+    xArc       *parc;
+{
+    DDXPointPtr points;
+    register DDXPointPtr pts;
+    int *widths;
+    register int *wids;
+    miArcSpanData *spdata;
+    Bool mustFree;
+    register miArcSpan *span;
+    register int xorg, yorgu, yorgl;
+    register int n;
+
+    yorgu = parc->height + pGC->lineWidth;
+    n = (sizeof(int) * 2) * yorgu;
+    widths = (int *)ALLOCATE_LOCAL(n + (sizeof(DDXPointRec) * 2) * yorgu);
+    if (!widths)
+       return;
+    points = (DDXPointPtr)((char *)widths + n);
+    spdata = miComputeWideEllipse((int)pGC->lineWidth, parc, &mustFree);
+    if (!spdata)
+    {
+       DEALLOCATE_LOCAL(widths);
+       return;
+    }
+    pts = points;
+    wids = widths;
+    span = spdata->spans;
+    xorg = parc->x + (parc->width >> 1);
+    yorgu = parc->y + (parc->height >> 1);
+    yorgl = yorgu + (parc->height & 1);
+    if (pGC->miTranslate)
+    {
+       xorg += pDraw->x;
+       yorgu += pDraw->y;
+       yorgl += pDraw->y;
+    }
+    yorgu -= spdata->k;
+    yorgl += spdata->k;
+    if (spdata->top)
+    {
+       pts->x = xorg;
+       pts->y = yorgu - 1;
+       pts++;
+       *wids++ = 1;
+       span++;
+    }
+    for (n = spdata->count1; --n >= 0; )
+    {
+       pts[0].x = xorg + span->lx;
+       pts[0].y = yorgu;
+       wids[0] = span->lw;
+       pts[1].x = pts[0].x;
+       pts[1].y = yorgl;
+       wids[1] = wids[0];
+       yorgu++;
+       yorgl--;
+       pts += 2;
+       wids += 2;
+       span++;
+    }
+    if (spdata->hole)
+    {
+       pts[0].x = xorg;
+       pts[0].y = yorgl;
+       wids[0] = 1;
+       pts++;
+       wids++;
+    }
+    for (n = spdata->count2; --n >= 0; )
+    {
+       pts[0].x = xorg + span->lx;
+       pts[0].y = yorgu;
+       wids[0] = span->lw;
+       pts[1].x = xorg + span->rx;
+       pts[1].y = pts[0].y;
+       wids[1] = span->rw;
+       pts[2].x = pts[0].x;
+       pts[2].y = yorgl;
+       wids[2] = wids[0];
+       pts[3].x = pts[1].x;
+       pts[3].y = pts[2].y;
+       wids[3] = wids[1];
+       yorgu++;
+       yorgl--;
+       pts += 4;
+       wids += 4;
+       span++;
+    }
+    if (spdata->bot)
+    {
+       if (span->rw <= 0)
+       {
+           pts[0].x = xorg + span->lx;
+           pts[0].y = yorgu;
+           wids[0] = span->lw;
+           pts++;
+           wids++;
+       }       
+       else
+       {
+           pts[0].x = xorg + span->lx;
+           pts[0].y = yorgu;
+           wids[0] = span->lw;
+           pts[1].x = xorg + span->rx;
+           pts[1].y = pts[0].y;
+           wids[1] = span->rw;
+           pts += 2;
+           wids += 2;
+       }
+    }
+    if (mustFree)
+       xfree(spdata);
+    (*pGC->ops->FillSpans)(pDraw, pGC, pts - points, points, widths, FALSE);
+
+    DEALLOCATE_LOCAL(widths);
+}
+
+/*
+ * miPolyArc strategy:
+ *
+ * If arc is zero width and solid, we don't have to worry about the rasterop
+ * or join styles.  For wide solid circles, we use a fast integer algorithm.
+ * For wide solid ellipses, we use special case floating point code.
+ * Otherwise, we set up pDrawTo and pGCTo according to the rasterop, then
+ * draw using pGCTo and pDrawTo.  If the raster-op was "tricky," that is,
+ * if it involves the destination, then we use PushPixels to move the bits
+ * from the scratch drawable to pDraw. (See the wide line code for a
+ * fuller explanation of this.)
+ */
+
+void
+miPolyArc(pDraw, pGC, narcs, parcs)
+    DrawablePtr        pDraw;
+    GCPtr      pGC;
+    int                narcs;
+    xArc       *parcs;
+{
+    register int               i;
+    xArc                       *parc;
+    int                                xMin, xMax, yMin, yMax;
+    int                                pixmapWidth, pixmapHeight;
+    int                                xOrg, yOrg;
+    int                                width;
+    Bool                       fTricky;
+    DrawablePtr                        pDrawTo;
+    CARD32                     fg, bg;
+    GCPtr                      pGCTo;
+    miPolyArcPtr               polyArcs;
+    int                                cap[2], join[2];
+    int                                iphase;
+    int                                halfWidth;
+
+    width = pGC->lineWidth;
+    if(width == 0 && pGC->lineStyle == LineSolid)
+    {
+       for(i = narcs, parc = parcs; --i >= 0; parc++)
+           miArcSegment( pDraw, pGC, *parc,
+           (miArcFacePtr) 0, (miArcFacePtr) 0 );
+       fillSpans (pDraw, pGC);
+    }
+    else 
+    {
+       if ((pGC->lineStyle == LineSolid) && narcs)
+       {
+           while (parcs->width && parcs->height &&
+                  (parcs->angle2 >= FULLCIRCLE ||
+                   parcs->angle2 <= -FULLCIRCLE))
+           {
+               miFillWideEllipse(pDraw, pGC, parcs);
+               if (!--narcs)
+                   return;
+               parcs++;
+           }
+       }
+
+       /* Set up pDrawTo and pGCTo based on the rasterop */
+       switch(pGC->alu)
+       {
+         case GXclear:         /* 0 */
+         case GXcopy:          /* src */
+         case GXcopyInverted:  /* NOT src */
+         case GXset:           /* 1 */
+           fTricky = FALSE;
+           pDrawTo = pDraw;
+           pGCTo = pGC;
+           break;
+         default:
+           fTricky = TRUE;
+
+           /* find bounding box around arcs */
+           xMin = yMin = MAXSHORT;
+           xMax = yMax = MINSHORT;
+
+           for(i = narcs, parc = parcs; --i >= 0; parc++)
+           {
+               xMin = min (xMin, parc->x);
+               yMin = min (yMin, parc->y);
+               xMax = max (xMax, (parc->x + (int) parc->width));
+               yMax = max (yMax, (parc->y + (int) parc->height));
+           }
+
+           /* expand box to deal with line widths */
+           halfWidth = (width + 1)/2;
+           xMin -= halfWidth;
+           yMin -= halfWidth;
+           xMax += halfWidth;
+           yMax += halfWidth;
+
+           /* compute pixmap size; limit it to size of drawable */
+           xOrg = max(xMin, 0);
+           yOrg = max(yMin, 0);
+           pixmapWidth = min(xMax, pDraw->width) - xOrg;
+           pixmapHeight = min(yMax, pDraw->height) - yOrg;
+
+           /* if nothing left, return */
+           if ( (pixmapWidth <= 0) || (pixmapHeight <= 0) ) return;
+
+           for(i = narcs, parc = parcs; --i >= 0; parc++)
+           {
+               parc->x -= xOrg;
+               parc->y -= yOrg;
+           }
+           if (pGC->miTranslate)
+           {
+               xOrg += pDraw->x;
+               yOrg += pDraw->y;
+           }
+
+           /* set up scratch GC */
+
+           pGCTo = GetScratchGC(1, pDraw->pScreen);
+           if (!pGCTo)
+               return;
+           gcvals[GCValsFunction] = GXcopy;
+           gcvals[GCValsForeground] = 1;
+           gcvals[GCValsBackground] = 0;
+           gcvals[GCValsLineWidth] = pGC->lineWidth;
+           gcvals[GCValsCapStyle] = pGC->capStyle;
+           gcvals[GCValsJoinStyle] = pGC->joinStyle;
+           dixChangeGC(NullClient, pGCTo, GCValsMask, gcvals, NULL);
+    
+           /* allocate a 1 bit deep pixmap of the appropriate size, and
+            * validate it */
+           pDrawTo = (DrawablePtr)(*pDraw->pScreen->CreatePixmap)
+                               (pDraw->pScreen, pixmapWidth, pixmapHeight, 1);
+           if (!pDrawTo)
+           {
+               FreeScratchGC(pGCTo);
+               return;
+           }
+           ValidateGC(pDrawTo, pGCTo);
+           miClearDrawable(pDrawTo, pGCTo);
+       }
+
+       fg = pGC->fgPixel;
+       bg = pGC->bgPixel;
+       if ((pGC->fillStyle == FillTiled) ||
+           (pGC->fillStyle == FillOpaqueStippled))
+           bg = fg; /* the protocol sez these don't cause color changes */
+
+       polyArcs = miComputeArcs (parcs, narcs, pGC);
+
+       if (!polyArcs)
+       {
+           if (fTricky) {
+               (*pDraw->pScreen->DestroyPixmap) ((PixmapPtr)pDrawTo);
+               FreeScratchGC (pGCTo);
+           }
+           return;
+       }
+
+       cap[0] = cap[1] = 0;
+       join[0] = join[1] = 0;
+       for (iphase = ((pGC->lineStyle == LineDoubleDash) ? 1 : 0);
+            iphase >= 0;
+            iphase--)
+       {
+           if (iphase == 1) {
+               dixChangeGC (NullClient, pGC, GCForeground, &bg, NULL);
+               ValidateGC (pDraw, pGC);
+           } else if (pGC->lineStyle == LineDoubleDash) {
+               dixChangeGC (NullClient, pGC, GCForeground, &fg, NULL);
+               ValidateGC (pDraw, pGC);
+           }
+           for (i = 0; i < polyArcs[iphase].narcs; i++) {
+               miArcDataPtr    arcData;
+
+               arcData = &polyArcs[iphase].arcs[i];
+               miArcSegment(pDrawTo, pGCTo, arcData->arc,
+                            &arcData->bounds[RIGHT_END],
+                            &arcData->bounds[LEFT_END]);
+               if (polyArcs[iphase].arcs[i].render) {
+                   fillSpans (pDrawTo, pGCTo);
+                   /*
+                    * don't cap self-joining arcs
+                    */
+                   if (polyArcs[iphase].arcs[i].selfJoin &&
+                       cap[iphase] < polyArcs[iphase].arcs[i].cap)
+                       cap[iphase]++;
+                   while (cap[iphase] < polyArcs[iphase].arcs[i].cap) {
+                       int     arcIndex, end;
+                       miArcDataPtr    arcData0;
+
+                       arcIndex = polyArcs[iphase].caps[cap[iphase]].arcIndex;
+                       end = polyArcs[iphase].caps[cap[iphase]].end;
+                       arcData0 = &polyArcs[iphase].arcs[arcIndex];
+                       miArcCap (pDrawTo, pGCTo,
+                                 &arcData0->bounds[end], end,
+                                 arcData0->arc.x, arcData0->arc.y,
+                                 (double) arcData0->arc.width / 2.0,
+                                 (double) arcData0->arc.height / 2.0);
+                       ++cap[iphase];
+                   }
+                   while (join[iphase] < polyArcs[iphase].arcs[i].join) {
+                       int     arcIndex0, arcIndex1, end0, end1;
+                       int     phase0, phase1;
+                       miArcDataPtr    arcData0, arcData1;
+                       miArcJoinPtr    joinp;
+
+                       joinp = &polyArcs[iphase].joins[join[iphase]];
+                       arcIndex0 = joinp->arcIndex0;
+                       end0 = joinp->end0;
+                       arcIndex1 = joinp->arcIndex1;
+                       end1 = joinp->end1;
+                       phase0 = joinp->phase0;
+                       phase1 = joinp->phase1;
+                       arcData0 = &polyArcs[phase0].arcs[arcIndex0];
+                       arcData1 = &polyArcs[phase1].arcs[arcIndex1];
+                       miArcJoin (pDrawTo, pGCTo,
+                                 &arcData0->bounds[end0],
+                                 &arcData1->bounds[end1],
+                                 arcData0->arc.x, arcData0->arc.y,
+                                 (double) arcData0->arc.width / 2.0,
+                                 (double) arcData0->arc.height / 2.0,
+                                 arcData1->arc.x, arcData1->arc.y,
+                                 (double) arcData1->arc.width / 2.0,
+                                 (double) arcData1->arc.height / 2.0);
+                       ++join[iphase];
+                   }
+                   if (fTricky) {
+                       if (pGC->serialNumber != pDraw->serialNumber)
+                           ValidateGC (pDraw, pGC);
+                       (*pGC->ops->PushPixels) (pGC, (PixmapPtr)pDrawTo,
+                                pDraw, pixmapWidth, pixmapHeight, xOrg, yOrg);
+                       miClearDrawable ((DrawablePtr) pDrawTo, pGCTo);
+                   }
+               }
+           }
+       }
+       miFreeArcs(polyArcs, pGC);
+
+       if(fTricky)
+       {
+           (*pGCTo->pScreen->DestroyPixmap)((PixmapPtr)pDrawTo);
+           FreeScratchGC(pGCTo);
+       }
+    }
+}
+
+static double
+angleBetween (center, point1, point2)
+       SppPointRec     center, point1, point2;
+{
+       double  a1, a2, a;
+       
+       /*
+        * reflect from X coordinates back to ellipse
+        * coordinates -- y increasing upwards
+        */
+       a1 = miDatan2 (- (point1.y - center.y), point1.x - center.x);
+       a2 = miDatan2 (- (point2.y - center.y), point2.x - center.x);
+       a = a2 - a1;
+       if (a <= -180.0)
+               a += 360.0;
+       else if (a > 180.0)
+               a -= 360.0;
+       return a;
+}
+
+static void
+translateBounds (b, x, y, fx, fy)
+miArcFacePtr   b;
+int            x, y;
+double         fx, fy;
+{
+       fx += x;
+       fy += y;
+       b->clock.x -= fx;
+       b->clock.y -= fy;
+       b->center.x -= fx;
+       b->center.y -= fy;
+       b->counterClock.x -= fx;
+       b->counterClock.y -= fy;
+}
+
+static void
+miArcJoin (pDraw, pGC, pLeft, pRight,
+          xOrgLeft, yOrgLeft, xFtransLeft, yFtransLeft,
+          xOrgRight, yOrgRight, xFtransRight, yFtransRight)
+       DrawablePtr     pDraw;
+       GCPtr           pGC;
+       miArcFacePtr    pRight, pLeft;
+       int             xOrgRight, yOrgRight;
+       double          xFtransRight, yFtransRight;
+       int             xOrgLeft, yOrgLeft;
+       double          xFtransLeft, yFtransLeft;
+{
+       SppPointRec     center, corner, otherCorner;
+       SppPointRec     poly[5], e;
+       SppPointPtr     pArcPts;
+       int             cpt;
+       SppArcRec       arc;
+       miArcFaceRec    Right, Left;
+       int             polyLen;
+       int             xOrg, yOrg;
+       double          xFtrans, yFtrans;
+       double          a;
+       double          ae, ac2, ec2, bc2, de;
+       double          width;
+       
+       xOrg = (xOrgRight + xOrgLeft) / 2;
+       yOrg = (yOrgRight + yOrgLeft) / 2;
+       xFtrans = (xFtransLeft + xFtransRight) / 2;
+       yFtrans = (yFtransLeft + yFtransRight) / 2;
+       Right = *pRight;
+       translateBounds (&Right, xOrg - xOrgRight, yOrg - yOrgRight,
+                                xFtrans - xFtransRight, yFtrans - yFtransRight);
+       Left = *pLeft;
+       translateBounds (&Left, xOrg - xOrgLeft, yOrg - yOrgLeft,
+                                xFtrans - xFtransLeft, yFtrans - yFtransLeft);
+       pRight = &Right;
+       pLeft = &Left;
+
+       if (pRight->clock.x == pLeft->counterClock.x &&
+           pRight->clock.y == pLeft->counterClock.y)
+               return;
+       center = pRight->center;
+       if (0 <= (a = angleBetween (center, pRight->clock, pLeft->counterClock))
+           && a <= 180.0)
+       {
+               corner = pRight->clock;
+               otherCorner = pLeft->counterClock;
+       } else {
+               a = angleBetween (center, pLeft->clock, pRight->counterClock);
+               corner = pLeft->clock;
+               otherCorner = pRight->counterClock;
+       }
+       switch (pGC->joinStyle) {
+       case JoinRound:
+               width = (pGC->lineWidth ? (double)pGC->lineWidth : (double)1);
+
+               arc.x = center.x - width/2;
+               arc.y = center.y - width/2;
+               arc.width = width;
+               arc.height = width;
+               arc.angle1 = -miDatan2 (corner.y - center.y, corner.x - center.x);
+               arc.angle2 = a;
+               pArcPts = (SppPointPtr) xalloc (3 * sizeof (SppPointRec));
+               if (!pArcPts)
+                   return;
+               pArcPts[0].x = otherCorner.x;
+               pArcPts[0].y = otherCorner.y;
+               pArcPts[1].x = center.x;
+               pArcPts[1].y = center.y;
+               pArcPts[2].x = corner.x;
+               pArcPts[2].y = corner.y;
+               if( (cpt = miGetArcPts(&arc, 3, &pArcPts)) )
+               {
+                       /* by drawing with miFillSppPoly and setting the endpoints of the arc
+                        * to be the corners, we assure that the cap will meet up with the
+                        * rest of the line */
+                       miFillSppPoly(pDraw, pGC, cpt, pArcPts, xOrg, yOrg, xFtrans, yFtrans);
+               }
+               xfree(pArcPts);
+               return;
+       case JoinMiter:
+               /*
+                * don't miter arcs with less than 11 degrees between them
+                */
+               if (a < 169.0) {
+                       poly[0] = corner;
+                       poly[1] = center;
+                       poly[2] = otherCorner;
+                       bc2 = (corner.x - otherCorner.x) * (corner.x - otherCorner.x) +
+                             (corner.y - otherCorner.y) * (corner.y - otherCorner.y);
+                       ec2 = bc2 / 4;
+                       ac2 = (corner.x - center.x) * (corner.x - center.x) +
+                             (corner.y - center.y) * (corner.y - center.y);
+                       ae = sqrt (ac2 - ec2);
+                       de = ec2 / ae;
+                       e.x = (corner.x + otherCorner.x) / 2;
+                       e.y = (corner.y + otherCorner.y) / 2;
+                       poly[3].x = e.x + de * (e.x - center.x) / ae;
+                       poly[3].y = e.y + de * (e.y - center.y) / ae;
+                       poly[4] = corner;
+                       polyLen = 5;
+                       break;
+               }
+       case JoinBevel:
+               poly[0] = corner;
+               poly[1] = center;
+               poly[2] = otherCorner;
+               poly[3] = corner;
+               polyLen = 4;
+               break;
+       }
+       miFillSppPoly (pDraw, pGC, polyLen, poly, xOrg, yOrg, xFtrans, yFtrans);
+}
+
+/*ARGSUSED*/
+static void
+miArcCap (pDraw, pGC, pFace, end, xOrg, yOrg, xFtrans, yFtrans)
+       DrawablePtr     pDraw;
+       GCPtr           pGC;
+       miArcFacePtr    pFace;
+       int             end;
+       int             xOrg, yOrg;
+       double          xFtrans, yFtrans;
+{
+       SppPointRec     corner, otherCorner, center, endPoint, poly[5];
+
+       corner = pFace->clock;
+       otherCorner = pFace->counterClock;
+       center = pFace->center;
+       switch (pGC->capStyle) {
+       case CapProjecting:
+               poly[0].x = otherCorner.x;
+               poly[0].y = otherCorner.y;
+               poly[1].x = corner.x;
+               poly[1].y = corner.y;
+               poly[2].x = corner.x -
+                               (center.y - corner.y);
+               poly[2].y = corner.y +
+                               (center.x - corner.x);
+               poly[3].x = otherCorner.x -
+                               (otherCorner.y - center.y);
+               poly[3].y = otherCorner.y +
+                               (otherCorner.x - center.x);
+               poly[4].x = otherCorner.x;
+               poly[4].y = otherCorner.y;
+               miFillSppPoly (pDraw, pGC, 5, poly, xOrg, yOrg, xFtrans, yFtrans);
+               break;
+       case CapRound:
+               /*
+                * miRoundCap just needs these to be unequal.
+                */
+               endPoint = center;
+               endPoint.x = endPoint.x + 100;
+               miRoundCap (pDraw, pGC, center, endPoint, corner, otherCorner, 0,
+                           -xOrg, -yOrg, xFtrans, yFtrans);
+               break;
+       }
+}
+
+/* MIROUNDCAP -- a private helper function
+ * Put Rounded cap on end. pCenter is the center of this end of the line
+ * pEnd is the center of the other end of the line. pCorner is one of the
+ * two corners at this end of the line.  
+ * NOTE:  pOtherCorner must be counter-clockwise from pCorner.
+ */
+/*ARGSUSED*/
+static void
+miRoundCap(pDraw, pGC, pCenter, pEnd, pCorner, pOtherCorner, fLineEnd,
+     xOrg, yOrg, xFtrans, yFtrans)
+    DrawablePtr        pDraw;
+    GCPtr      pGC;
+    SppPointRec        pCenter, pEnd;
+    SppPointRec        pCorner, pOtherCorner;
+    int                fLineEnd, xOrg, yOrg;
+    double     xFtrans, yFtrans;
+{
+    int                cpt;
+    double     width;
+    double     miDatan2 ();
+    SppArcRec  arc;
+    SppPointPtr        pArcPts;
+
+    width = (pGC->lineWidth ? (double)pGC->lineWidth : (double)1);
+
+    arc.x = pCenter.x - width/2;
+    arc.y = pCenter.y - width/2;
+    arc.width = width;
+    arc.height = width;
+    arc.angle1 = -miDatan2 (pCorner.y - pCenter.y, pCorner.x - pCenter.x);
+    if(PTISEQUAL(pCenter, pEnd))
+       arc.angle2 = - 180.0;
+    else {
+       arc.angle2 = -miDatan2 (pOtherCorner.y - pCenter.y, pOtherCorner.x - pCenter.x) - arc.angle1;
+       if (arc.angle2 < 0)
+           arc.angle2 += 360.0;
+    }
+    pArcPts = (SppPointPtr) NULL;
+    if( (cpt = miGetArcPts(&arc, 0, &pArcPts)) )
+    {
+       /* by drawing with miFillSppPoly and setting the endpoints of the arc
+        * to be the corners, we assure that the cap will meet up with the
+        * rest of the line */
+       miFillSppPoly(pDraw, pGC, cpt, pArcPts, -xOrg, -yOrg, xFtrans, yFtrans);
+    }
+    xfree(pArcPts);
+}
+
+/*
+ * To avoid inaccuracy at the cardinal points, use trig functions
+ * which are exact for those angles
+ */
+
+#ifndef M_PI
+#define M_PI   3.14159265358979323846
+#endif
+#ifndef M_PI_2
+#define M_PI_2 1.57079632679489661923
+#endif
+
+# define Dsin(d)       ((d) == 0.0 ? 0.0 : ((d) == 90.0 ? 1.0 : sin(d*M_PI/180.0)))
+# define Dcos(d)       ((d) == 0.0 ? 1.0 : ((d) == 90.0 ? 0.0 : cos(d*M_PI/180.0)))
+# define mod(a,b)      ((a) >= 0 ? (a) % (b) : (b) - (-a) % (b))
+
+static double
+miDcos (a)
+double a;
+{
+       int     i;
+
+       if (floor (a/90) == a/90) {
+               i = (int) (a/90.0);
+               switch (mod (i, 4)) {
+               case 0: return 1;
+               case 1: return 0;
+               case 2: return -1;
+               case 3: return 0;
+               }
+       }
+       return cos (a * M_PI / 180.0);
+}
+
+static double
+miDsin (a)
+double a;
+{
+       int     i;
+
+       if (floor (a/90) == a/90) {
+               i = (int) (a/90.0);
+               switch (mod (i, 4)) {
+               case 0: return 0;
+               case 1: return 1;
+               case 2: return 0;
+               case 3: return -1;
+               }
+       }
+       return sin (a * M_PI / 180.0);
+}
+
+static double
+miDasin (v)
+double v;
+{
+    if (v == 0)
+       return 0.0;
+    if (v == 1.0)
+       return 90.0;
+    if (v == -1.0)
+       return -90.0;
+    return asin(v) * (180.0 / M_PI);
+}
+
+static double
+miDatan2 (dy, dx)
+double dy, dx;
+{
+    if (dy == 0) {
+       if (dx >= 0)
+           return 0.0;
+       return 180.0;
+    } else if (dx == 0) {
+       if (dy > 0)
+           return 90.0;
+       return -90.0;
+    } else if (Fabs (dy) == Fabs (dx)) {
+       if (dy > 0) {
+           if (dx > 0)
+               return 45.0;
+           return 135.0;
+       } else {
+           if (dx > 0)
+               return 315.0;
+           return 225.0;
+       }
+    } else {
+       return atan2 (dy, dx) * (180.0 / M_PI);
+    }
+}
+
+/* MIGETARCPTS -- Converts an arc into a set of line segments -- a helper
+ * routine for filled arc and line (round cap) code.
+ * Returns the number of points in the arc.  Note that it takes a pointer
+ * to a pointer to where it should put the points and an index (cpt).
+ * This procedure allocates the space necessary to fit the arc points.
+ * Sometimes it's convenient for those points to be at the end of an existing
+ * array. (For example, if we want to leave a spare point to make sectors
+ * instead of segments.)  So we pass in the xalloc()ed chunk that contains the
+ * array and an index saying where we should start stashing the points.
+ * If there isn't an array already, we just pass in a null pointer and 
+ * count on xrealloc() to handle the null pointer correctly.
+ */
+static int
+miGetArcPts(parc, cpt, ppPts)
+    SppArcPtr  parc;   /* points to an arc */
+    int                cpt;    /* number of points already in arc list */
+    SppPointPtr        *ppPts; /* pointer to pointer to arc-list -- modified */
+{
+    double     st,     /* Start Theta, start angle */
+                et,    /* End Theta, offset from start theta */
+               dt,     /* Delta Theta, angle to sweep ellipse */
+               cdt,    /* Cos Delta Theta, actually 2 cos(dt) */
+               x0, y0, /* the recurrence formula needs two points to start */
+               x1, y1,
+               x2, y2, /* this will be the new point generated */
+               xc, yc; /* the center point */
+    int                count, i;
+    SppPointPtr        poly;
+    DDXPointRec last;          /* last point on integer boundaries */
+
+    /* The spec says that positive angles indicate counterclockwise motion.
+     * Given our coordinate system (with 0,0 in the upper left corner), 
+     * the screen appears flipped in Y.  The easiest fix is to negate the
+     * angles given */
+    
+    st = - parc->angle1;
+
+    et = - parc->angle2;
+
+    /* Try to get a delta theta that is within 1/2 pixel.  Then adjust it
+     * so that it divides evenly into the total.
+     * I'm just using cdt 'cause I'm lazy.
+     */
+    cdt = parc->width;
+    if (parc->height > cdt)
+       cdt = parc->height;
+    cdt /= 2.0;
+    if(cdt <= 0)
+       return 0;
+    if (cdt < 1.0)
+       cdt = 1.0;
+    dt = miDasin ( 1.0 / cdt ); /* minimum step necessary */
+    count = et/dt;
+    count = abs(count) + 1;
+    dt = et/count;     
+    count++;
+
+    cdt = 2 * miDcos(dt);
+    if (!(poly = (SppPointPtr) xrealloc((pointer)*ppPts,
+                                       (cpt + count) * sizeof(SppPointRec))))
+       return(0);
+    *ppPts = poly;
+
+    xc = parc->width/2.0;              /* store half width and half height */
+    yc = parc->height/2.0;
+    
+    x0 = xc * miDcos(st);
+    y0 = yc * miDsin(st);
+    x1 = xc * miDcos(st + dt);
+    y1 = yc * miDsin(st + dt);
+    xc += parc->x;             /* by adding initial point, these become */
+    yc += parc->y;             /* the center point */
+
+    poly[cpt].x = (xc + x0);
+    poly[cpt].y = (yc + y0);
+    last.x = ROUNDTOINT( poly[cpt + 1].x = (xc + x1) );
+    last.y = ROUNDTOINT( poly[cpt + 1].y = (yc + y1) );
+
+    for(i = 2; i < count; i++)
+    {
+       x2 = cdt * x1 - x0;
+       y2 = cdt * y1 - y0;
+
+       poly[cpt + i].x = (xc + x2);
+       poly[cpt + i].y = (yc + y2);
+
+       x0 = x1; y0 = y1;
+       x1 = x2; y1 = y2;
+    }
+    /* adjust the last point */
+    if (abs(parc->angle2) >= 360.0)
+       poly[cpt +i -1] = poly[0];
+    else {
+       poly[cpt +i -1].x = (miDcos(st + et) * parc->width/2.0 + xc);
+       poly[cpt +i -1].y = (miDsin(st + et) * parc->height/2.0 + yc);
+    }
+
+    return(count);
+}
+
+struct arcData {
+       double  x0, y0, x1, y1;
+       int     selfJoin;
+};
+
+# define ADD_REALLOC_STEP      20
+
+static void
+addCap (capsp, ncapsp, sizep, end, arcIndex)
+       miArcCapPtr     *capsp;
+       int             *ncapsp, *sizep;
+       int             end, arcIndex;
+{
+       int newsize;
+       miArcCapPtr     cap;
+
+       if (*ncapsp == *sizep)
+       {
+           newsize = *sizep + ADD_REALLOC_STEP;
+           cap = (miArcCapPtr) xrealloc (*capsp,
+                                         newsize * sizeof (**capsp));
+           if (!cap)
+               return;
+           *sizep = newsize;
+           *capsp = cap;
+       }
+       cap = &(*capsp)[*ncapsp];
+       cap->end = end;
+       cap->arcIndex = arcIndex;
+       ++*ncapsp;
+}
+
+static void
+addJoin (joinsp, njoinsp, sizep, end0, index0, phase0, end1, index1, phase1)
+       miArcJoinPtr    *joinsp;
+       int             *njoinsp, *sizep;
+       int             end0, index0, phase0, end1, index1, phase1;
+{
+       int newsize;
+       miArcJoinPtr    join;
+
+       if (*njoinsp == *sizep)
+       {
+           newsize = *sizep + ADD_REALLOC_STEP;
+           join = (miArcJoinPtr) xrealloc (*joinsp,
+                                           newsize * sizeof (**joinsp));
+           if (!join)
+               return;
+           *sizep = newsize;
+           *joinsp = join;
+       }
+       join = &(*joinsp)[*njoinsp];
+       join->end0 = end0;
+       join->arcIndex0 = index0;
+       join->phase0 = phase0;
+       join->end1 = end1;
+       join->arcIndex1 = index1;
+       join->phase1 = phase1;
+       ++*njoinsp;
+}
+
+static miArcDataPtr
+addArc (arcsp, narcsp, sizep, xarc)
+       miArcDataPtr    *arcsp;
+       int             *narcsp, *sizep;
+       xArc            *xarc;
+{
+       int newsize;
+       miArcDataPtr    arc;
+
+       if (*narcsp == *sizep)
+       {
+           newsize = *sizep + ADD_REALLOC_STEP;
+           arc = (miArcDataPtr) xrealloc (*arcsp,
+                                          newsize * sizeof (**arcsp));
+           if (!arc)
+               return (miArcDataPtr)NULL;
+           *sizep = newsize;
+           *arcsp = arc;
+       }
+       arc = &(*arcsp)[*narcsp];
+       arc->arc = *xarc;
+       ++*narcsp;
+       return arc;
+}
+
+static void
+miFreeArcs(arcs, pGC)
+    miPolyArcPtr arcs;
+    GCPtr pGC;
+{
+       int iphase;
+
+       for (iphase = ((pGC->lineStyle == LineDoubleDash) ? 1 : 0);
+            iphase >= 0;
+            iphase--)
+       {
+           if (arcs[iphase].narcs > 0)
+               xfree(arcs[iphase].arcs);
+           if (arcs[iphase].njoins > 0)
+               xfree(arcs[iphase].joins);
+           if (arcs[iphase].ncaps > 0)
+               xfree(arcs[iphase].caps);
+       }
+       xfree(arcs);
+}
+
+/*
+ * map angles to radial distance.  This only deals with the first quadrant
+ */
+
+/*
+ * a polygonal approximation to the arc for computing arc lengths
+ */
+
+# define DASH_MAP_SIZE 91
+
+# define dashIndexToAngle(di)  ((((double) (di)) * 90.0) / ((double) DASH_MAP_SIZE - 1))
+# define xAngleToDashIndex(xa) ((((long) (xa)) * (DASH_MAP_SIZE - 1)) / (90 * 64))
+# define dashIndexToXAngle(di) ((((long) (di)) * (90 * 64)) / (DASH_MAP_SIZE - 1))
+# define dashXAngleStep        (((double) (90 * 64)) / ((double) (DASH_MAP_SIZE - 1)))
+
+typedef struct {
+       double  map[DASH_MAP_SIZE];
+} dashMap;
+
+static void
+computeDashMap (arcp, map)
+       xArc    *arcp;
+       dashMap *map;
+{
+       int     di;
+       double  a, x, y, prevx, prevy, dist;
+
+       for (di = 0; di < DASH_MAP_SIZE; di++) {
+               a = dashIndexToAngle (di);
+               x = ((double) arcp->width / 2.0) * miDcos (a);
+               y = ((double) arcp->height / 2.0) * miDsin (a);
+               if (di == 0) {
+                       map->map[di] = 0.0;
+               } else {
+                       dist = hypot (x - prevx, y - prevy);
+                       map->map[di] = map->map[di - 1] + dist;
+               }
+               prevx = x;
+               prevy = y;
+       }
+}
+
+typedef enum {HORIZONTAL, VERTICAL, OTHER} arcTypes;
+
+/* this routine is a bit gory */
+
+static miPolyArcPtr
+miComputeArcs (parcs, narcs, pGC)
+       xArc    *parcs;
+       int     narcs;
+       GCPtr   pGC;
+{
+       int             isDashed, isDoubleDash;
+       int             dashOffset;
+       miPolyArcPtr    arcs;
+       int             start, i, j, k, nexti, nextk;
+       int             joinSize[2];
+       int             capSize[2];
+       int             arcSize[2];
+       int             angle2;
+       double          a0, a1;
+       struct arcData  *data;
+       miArcDataPtr    arc;
+       xArc            xarc;
+       int             iphase, prevphase, joinphase;
+       int             arcsJoin;
+       int             selfJoin;
+
+       int             iDash, dashRemaining;
+       int             iDashStart, dashRemainingStart, iphaseStart;
+       int             startAngle, spanAngle, endAngle, backwards;
+       int             prevDashAngle, dashAngle;
+       dashMap         map;
+
+       isDashed = !(pGC->lineStyle == LineSolid);
+       isDoubleDash = (pGC->lineStyle == LineDoubleDash);
+       dashOffset = pGC->dashOffset;
+
+       data = (struct arcData *) ALLOCATE_LOCAL (narcs * sizeof (struct arcData));
+       if (!data)
+           return (miPolyArcPtr)NULL;
+       arcs = (miPolyArcPtr) xalloc (sizeof (*arcs) * (isDoubleDash ? 2 : 1));
+       if (!arcs)
+       {
+           DEALLOCATE_LOCAL(data);
+           return (miPolyArcPtr)NULL;
+       }
+       for (i = 0; i < narcs; i++) {
+               a0 = todeg (parcs[i].angle1);
+               angle2 = parcs[i].angle2;
+               if (angle2 > FULLCIRCLE)
+                       angle2 = FULLCIRCLE;
+               else if (angle2 < -FULLCIRCLE)
+                       angle2 = -FULLCIRCLE;
+               data[i].selfJoin = angle2 == FULLCIRCLE || angle2 == -FULLCIRCLE;
+               a1 = todeg (parcs[i].angle1 + angle2);
+               data[i].x0 = parcs[i].x + (double) parcs[i].width / 2 * (1 + miDcos (a0));
+               data[i].y0 = parcs[i].y + (double) parcs[i].height / 2 * (1 - miDsin (a0));
+               data[i].x1 = parcs[i].x + (double) parcs[i].width / 2 * (1 + miDcos (a1));
+               data[i].y1 = parcs[i].y + (double) parcs[i].height / 2 * (1 - miDsin (a1));
+       }
+
+       for (iphase = 0; iphase < (isDoubleDash ? 2 : 1); iphase++) {
+               arcs[iphase].njoins = 0;
+               arcs[iphase].joins = 0;
+               joinSize[iphase] = 0;
+               
+               arcs[iphase].ncaps = 0;
+               arcs[iphase].caps = 0;
+               capSize[iphase] = 0;
+               
+               arcs[iphase].narcs = 0;
+               arcs[iphase].arcs = 0;
+               arcSize[iphase] = 0;
+       }
+
+       iphase = 0;
+       if (isDashed) {
+               iDash = 0;
+               dashRemaining = pGC->dash[0];
+               while (dashOffset > 0) {
+                       if (dashOffset >= dashRemaining) {
+                               dashOffset -= dashRemaining;
+                               iphase = iphase ? 0 : 1;
+                               iDash++;
+                               if (iDash == pGC->numInDashList)
+                                   iDash = 0;
+                               dashRemaining = pGC->dash[iDash];
+                       } else {
+                               dashRemaining -= dashOffset;
+                               dashOffset = 0;
+                       }
+               }
+               iDashStart = iDash;
+               dashRemainingStart = dashRemaining;
+       }
+       iphaseStart = iphase;
+
+       for (i = narcs - 1; i >= 0; i--) {
+               j = i + 1;
+               if (j == narcs)
+                       j = 0;
+               if (data[i].selfJoin || i == j ||
+                    (UNEQUAL (data[i].x1, data[j].x0) ||
+                     UNEQUAL (data[i].y1, data[j].y0)))
+               {
+                       if (iphase == 0 || isDoubleDash)
+                               addCap (&arcs[iphase].caps, &arcs[iphase].ncaps,
+                                       &capSize[iphase], RIGHT_END, 0);
+                       break;
+               }
+       }
+       start = i + 1;
+       if (start == narcs)
+               start = 0;
+       i = start;
+       for (;;) {
+               j = i + 1;
+               if (j == narcs)
+                       j = 0;
+               nexti = i+1;
+               if (nexti == narcs)
+                       nexti = 0;
+               if (isDashed) {
+                       /*
+                       ** deal with dashed arcs.  Use special rules for certain 0 area arcs.
+                       ** Presumably, the other 0 area arcs still aren't done right.
+                       */
+                       arcTypes        arcType = OTHER;
+                       CARD16          thisLength;
+
+                       if (parcs[i].height == 0
+                           && (parcs[i].angle1 % FULLCIRCLE) == 0x2d00
+                           && parcs[i].angle2 == 0x2d00) 
+                               arcType = HORIZONTAL;
+                       else if (parcs[i].width == 0
+                           && (parcs[i].angle1 % FULLCIRCLE) == 0x1680
+                           && parcs[i].angle2 == 0x2d00)
+                               arcType = VERTICAL;
+                       if (arcType == OTHER) {
+                               /*
+                                * precompute an approximation map
+                                */
+                               computeDashMap (&parcs[i], &map);
+                               /*
+                                * compute each individual dash segment using the path
+                                * length function
+                                */
+                               startAngle = parcs[i].angle1;
+                               spanAngle = parcs[i].angle2;
+                               if (spanAngle > FULLCIRCLE)
+                                       spanAngle = FULLCIRCLE;
+                               else if (spanAngle < -FULLCIRCLE)
+                                       spanAngle = -FULLCIRCLE;
+                               if (startAngle < 0)
+                                       startAngle = FULLCIRCLE - (-startAngle) % FULLCIRCLE;
+                               if (startAngle >= FULLCIRCLE)
+                                       startAngle = startAngle % FULLCIRCLE;
+                               endAngle = startAngle + spanAngle;
+                               backwards = spanAngle < 0;
+                       } else {
+                               xarc = parcs[i];
+                               if (arcType == VERTICAL) {
+                                       xarc.angle1 = 0x1680;
+                                       startAngle = parcs[i].y;
+                                       endAngle = startAngle + parcs[i].height;
+                               } else {
+                                       xarc.angle1 = 0x2d00;
+                                       startAngle = parcs[i].x;
+                                       endAngle = startAngle + parcs[i].width;
+                               }
+                       }
+                       dashAngle = startAngle;
+                       selfJoin = data[i].selfJoin &&
+                                   (iphase == 0 || isDoubleDash);
+                       /*
+                        * add dashed arcs to each bucket
+                        */
+                       arc = 0;
+                       while (dashAngle != endAngle) {
+                               prevDashAngle = dashAngle;
+                               if (arcType == OTHER) {
+                                       dashAngle = computeAngleFromPath (prevDashAngle, endAngle,
+                                                               &map, &dashRemaining, backwards);
+                                       /* avoid troubles with huge arcs and small dashes */
+                                       if (dashAngle == prevDashAngle) {
+                                               if (backwards)
+                                                       dashAngle--;
+                                               else
+                                                       dashAngle++;
+                                       }
+                               } else {
+                                       thisLength = (dashAngle + dashRemaining <= endAngle) ? 
+                                           dashRemaining : endAngle - dashAngle;
+                                       if (arcType == VERTICAL) {
+                                               xarc.y = dashAngle;
+                                               xarc.height = thisLength;
+                                       } else {
+                                               xarc.x = dashAngle;
+                                               xarc.width = thisLength;
+                                       }
+                                       dashAngle += thisLength;
+                                       dashRemaining -= thisLength;
+                               }
+                               if (iphase == 0 || isDoubleDash) {
+                                       if (arcType == OTHER) {
+                                               xarc = parcs[i];
+                                               spanAngle = prevDashAngle;
+                                               if (spanAngle < 0)
+                                                   spanAngle = FULLCIRCLE - (-spanAngle) % FULLCIRCLE;
+                                               if (spanAngle >= FULLCIRCLE)
+                                                   spanAngle = spanAngle % FULLCIRCLE;
+                                               xarc.angle1 = spanAngle;
+                                               spanAngle = dashAngle - prevDashAngle;
+                                               if (backwards) {
+                                                       if (dashAngle > prevDashAngle)
+                                                               spanAngle = - FULLCIRCLE + spanAngle;
+                                               } else {
+                                                       if (dashAngle < prevDashAngle)
+                                                               spanAngle = FULLCIRCLE + spanAngle;
+                                               }
+                                               if (spanAngle > FULLCIRCLE)
+                                                   spanAngle = FULLCIRCLE;
+                                               if (spanAngle < -FULLCIRCLE)
+                                                   spanAngle = -FULLCIRCLE;
+                                               xarc.angle2 = spanAngle;
+                                       }
+                                       arc = addArc (&arcs[iphase].arcs, &arcs[iphase].narcs,
+                                                       &arcSize[iphase], &xarc);
+                                       if (!arc)
+                                           goto arcfail;
+                                       /*
+                                        * cap each end of an on/off dash
+                                        */
+                                       if (!isDoubleDash) {
+                                               if (prevDashAngle != startAngle) {
+                                                       addCap (&arcs[iphase].caps,
+                                                               &arcs[iphase].ncaps,
+                                                               &capSize[iphase], RIGHT_END,
+                                                               arc - arcs[iphase].arcs);
+                                                       
+                                               }
+                                               if (dashAngle != endAngle) {
+                                                       addCap (&arcs[iphase].caps,
+                                                               &arcs[iphase].ncaps,
+                                                               &capSize[iphase], LEFT_END,
+                                                               arc - arcs[iphase].arcs);
+                                               }
+                                       }
+                                       arc->cap = arcs[iphase].ncaps;
+                                       arc->join = arcs[iphase].njoins;
+                                       arc->render = 0;
+                                       arc->selfJoin = 0;
+                                       if (dashAngle == endAngle)
+                                               arc->selfJoin = selfJoin;
+                               }
+                               prevphase = iphase;
+                               if (dashRemaining <= 0) {
+                                       ++iDash;
+                                       if (iDash == pGC->numInDashList)
+                                               iDash = 0;
+                                       iphase = iphase ? 0:1;
+                                       dashRemaining = pGC->dash[iDash];
+                               }
+                       }
+                       /*
+                        * make sure a place exists for the position data when
+                        * drawing a zero-length arc
+                        */
+                       if (startAngle == endAngle) {
+                               prevphase = iphase;
+                               if (!isDoubleDash && iphase == 1)
+                                       prevphase = 0;
+                               arc = addArc (&arcs[prevphase].arcs, &arcs[prevphase].narcs,
+                                             &arcSize[prevphase], &parcs[i]);
+                               if (!arc)
+                                   goto arcfail;
+                               arc->join = arcs[prevphase].njoins;
+                               arc->cap = arcs[prevphase].ncaps;
+                               arc->selfJoin = data[i].selfJoin;
+                       }
+               } else {
+                       arc = addArc (&arcs[iphase].arcs, &arcs[iphase].narcs,
+                                     &arcSize[iphase], &parcs[i]);
+                       if (!arc)
+                           goto arcfail;
+                       arc->join = arcs[iphase].njoins;
+                       arc->cap = arcs[iphase].ncaps;
+                       arc->selfJoin = data[i].selfJoin;
+                       prevphase = iphase;
+               }
+               if (prevphase == 0 || isDoubleDash)
+                       k = arcs[prevphase].narcs - 1;
+               if (iphase == 0 || isDoubleDash)
+                       nextk = arcs[iphase].narcs;
+               if (nexti == start) {
+                       nextk = 0;
+                       if (isDashed) {
+                               iDash = iDashStart;
+                               iphase = iphaseStart;
+                               dashRemaining = dashRemainingStart;
+                       }
+               }
+               arcsJoin = narcs > 1 && i != j &&
+                           ISEQUAL (data[i].x1, data[j].x0) &&
+                           ISEQUAL (data[i].y1, data[j].y0) &&
+                           !data[i].selfJoin && !data[j].selfJoin;
+               if (arc)
+               {
+                       if (arcsJoin)
+                               arc->render = 0;
+                       else
+                               arc->render = 1;
+               }
+               if (arcsJoin &&
+                   (prevphase == 0 || isDoubleDash) &&
+                   (iphase == 0 || isDoubleDash))
+               {
+                       joinphase = iphase;
+                       if (isDoubleDash) {
+                               if (nexti == start)
+                                       joinphase = iphaseStart;
+                               /*
+                                * if the join is right at the dash,
+                                * draw the join in foreground
+                                * This is because the foreground
+                                * arcs are computed second, the results
+                                * of which are needed to draw the join
+                                */
+                               if (joinphase != prevphase)
+                                       joinphase = 0;
+                       }
+                       if (joinphase == 0 || isDoubleDash) {
+                               addJoin (&arcs[joinphase].joins,
+                                        &arcs[joinphase].njoins,
+                                        &joinSize[joinphase],
+                                        LEFT_END, k, prevphase,
+                                        RIGHT_END, nextk, iphase);
+                               arc->join = arcs[prevphase].njoins;
+                       }
+               } else {
+                       /*
+                        * cap the left end of this arc
+                        * unless it joins itself
+                        */
+                       if ((prevphase == 0 || isDoubleDash) &&
+                           !arc->selfJoin)
+                       {
+                               addCap (&arcs[prevphase].caps, &arcs[prevphase].ncaps,
+                                       &capSize[prevphase], LEFT_END, k);
+                               arc->cap = arcs[prevphase].ncaps;
+                       }
+                       if (isDashed && !arcsJoin) {
+                               iDash = iDashStart;
+                               iphase = iphaseStart;
+                               dashRemaining = dashRemainingStart;
+                       }
+                       nextk = arcs[iphase].narcs;
+                       if (nexti == start) {
+                               nextk = 0;
+                               iDash = iDashStart;
+                               iphase = iphaseStart;
+                               dashRemaining = dashRemainingStart;
+                       }
+                       /*
+                        * cap the right end of the next arc.  If the
+                        * next arc is actually the first arc, only
+                        * cap it if it joins with this arc.  This
+                        * case will occur when the final dash segment
+                        * of an on/off dash is off.  Of course, this
+                        * cap will be drawn at a strange time, but that
+                        * hardly matters...
+                        */
+                       if ((iphase == 0 || isDoubleDash) &&
+                           (nexti != start || (arcsJoin && isDashed)))
+                               addCap (&arcs[iphase].caps, &arcs[iphase].ncaps,
+                                       &capSize[iphase], RIGHT_END, nextk);
+               }
+               i = nexti;
+               if (i == start)
+                       break;
+       }
+       /*
+        * make sure the last section is rendered
+        */
+       for (iphase = 0; iphase < (isDoubleDash ? 2 : 1); iphase++)
+               if (arcs[iphase].narcs > 0) {
+                       arcs[iphase].arcs[arcs[iphase].narcs-1].render = 1;
+                       arcs[iphase].arcs[arcs[iphase].narcs-1].join =
+                                arcs[iphase].njoins;
+                       arcs[iphase].arcs[arcs[iphase].narcs-1].cap =
+                                arcs[iphase].ncaps;
+               }
+       DEALLOCATE_LOCAL(data);
+       return arcs;
+arcfail:
+       miFreeArcs(arcs, pGC);
+       DEALLOCATE_LOCAL(data);
+       return (miPolyArcPtr)NULL;
+}
+
+static double
+angleToLength (angle, map)
+       int     angle;
+       dashMap *map;
+{
+       double  len, excesslen, sidelen = map->map[DASH_MAP_SIZE - 1], totallen;
+       int     di;
+       int     excess;
+       Bool    oddSide = FALSE;
+
+       totallen = 0;
+       if (angle >= 0) {
+               while (angle >= 90 * 64) {
+                       angle -= 90 * 64;
+                       totallen += sidelen;
+                       oddSide = !oddSide;
+               }
+       } else {
+               while (angle < 0) {
+                       angle += 90 * 64;
+                       totallen -= sidelen;
+                       oddSide = !oddSide;
+               }
+       }
+       if (oddSide)
+               angle = 90 * 64 - angle;
+               
+       di = xAngleToDashIndex (angle);
+       excess = angle - dashIndexToXAngle (di);
+
+       len = map->map[di];
+       /*
+        * linearly interpolate between this point and the next
+        */
+       if (excess > 0) {
+               excesslen = (map->map[di + 1] - map->map[di]) *
+                               ((double) excess) / dashXAngleStep;
+               len += excesslen;
+       }
+       if (oddSide)
+               totallen += (sidelen - len);
+       else
+               totallen += len;
+       return totallen;
+}
+
+/*
+ * len is along the arc, but may be more than one rotation
+ */
+
+static int
+lengthToAngle (len, map)
+       double  len;
+       dashMap *map;
+{
+       double  sidelen = map->map[DASH_MAP_SIZE - 1];
+       int     angle, angleexcess;
+       Bool    oddSide = FALSE;
+       int     a0, a1, a;
+
+       angle = 0;
+       /*
+        * step around the ellipse, subtracting sidelens and
+        * adding 90 degrees.  oddSide will tell if the
+        * map should be interpolated in reverse
+        */
+       if (len >= 0) {
+               if (sidelen == 0)
+                       return 2 * FULLCIRCLE;  /* infinity */
+               while (len >= sidelen) {
+                       angle += 90 * 64;
+                       len -= sidelen;
+                       oddSide = !oddSide;
+               }
+       } else {
+               if (sidelen == 0)
+                       return -2 * FULLCIRCLE; /* infinity */
+               while (len < 0) {
+                       angle -= 90 * 64;
+                       len += sidelen;
+                       oddSide = !oddSide;
+               }
+       }
+       if (oddSide)
+               len = sidelen - len;
+       a0 = 0;
+       a1 = DASH_MAP_SIZE - 1;
+       /*
+        * binary search for the closest pre-computed length
+        */
+       while (a1 - a0 > 1) {
+               a = (a0 + a1) / 2;
+               if (len > map->map[a])
+                       a0 = a;
+               else
+                       a1 = a;
+       }
+       angleexcess = dashIndexToXAngle (a0);
+       /*
+        * linearly interpolate to the next point
+        */
+       angleexcess += (len - map->map[a0]) /
+                       (map->map[a0+1] - map->map[a0]) * dashXAngleStep;
+       if (oddSide)
+               angle += (90 * 64) - angleexcess;
+       else
+               angle += angleexcess;
+       return angle;
+}
+
+/*
+ * compute the angle of an ellipse which cooresponds to
+ * the given path length.  Note that the correct solution
+ * to this problem is an eliptic integral, we'll punt and
+ * approximate (it's only for dashes anyway).  This
+ * approximation uses a polygon.
+ *
+ * The remaining portion of len is stored in *lenp -
+ * this will be negative if the arc extends beyond
+ * len and positive if len extends beyond the arc.
+ */
+
+static int
+computeAngleFromPath (startAngle, endAngle, map, lenp, backwards)
+       int     startAngle, endAngle;   /* normalized absolute angles in *64 degrees */
+       dashMap *map;
+       int     *lenp;
+       int     backwards;
+{
+       int     a0, a1, a;
+       double  len0;
+       int     len;
+
+       a0 = startAngle;
+       a1 = endAngle;
+       len = *lenp;
+       if (backwards) {
+               /*
+                * flip the problem around to always be
+                * forwards
+                */
+               a0 = FULLCIRCLE - a0;
+               a1 = FULLCIRCLE - a1;
+       }
+       if (a1 < a0)
+               a1 += FULLCIRCLE;
+       len0 = angleToLength (a0, map);
+       a = lengthToAngle (len0 + len, map);
+       if (a > a1) {
+               a = a1;
+               len -= angleToLength (a1, map) - len0;
+       } else
+               len = 0;
+       if (backwards)
+               a = FULLCIRCLE - a;
+       *lenp = len;
+       return a;
+}
+
+/*
+ * scan convert wide arcs.
+ */
+
+/*
+ * draw zero width/height arcs
+ */
+
+static void
+drawZeroArc (pDraw, pGC, tarc, lw, left, right)
+    DrawablePtr   pDraw;
+    GCPtr         pGC;
+    xArc          *tarc;
+    int                  lw;
+    miArcFacePtr       right, left;
+{
+       double  x0, y0, x1, y1, w, h, x, y;
+       double  xmax, ymax, xmin, ymin;
+       int     a0, a1;
+       double  a, startAngle, endAngle;
+       double  l, lx, ly;
+
+       l = lw / 2.0;
+       a0 = tarc->angle1;
+       a1 = tarc->angle2;
+       if (a1 > FULLCIRCLE)
+               a1 = FULLCIRCLE;
+       else if (a1 < -FULLCIRCLE)
+               a1 = -FULLCIRCLE;
+       w = (double)tarc->width / 2.0;
+       h = (double)tarc->height / 2.0;
+       /*
+        * play in X coordinates right away
+        */
+       startAngle = - ((double) a0 / 64.0);
+       endAngle = - ((double) (a0 + a1) / 64.0);
+       
+       xmax = -w;
+       xmin = w;
+       ymax = -h;
+       ymin = h;
+       a = startAngle;
+       for (;;)
+       {
+               x = w * miDcos(a);
+               y = h * miDsin(a);
+               if (a == startAngle)
+               {
+                       x0 = x;
+                       y0 = y;
+               }
+               if (a == endAngle)
+               {
+                       x1 = x;
+                       y1 = y;
+               }
+               if (x > xmax)
+                       xmax = x;
+               if (x < xmin)
+                       xmin = x;
+               if (y > ymax)
+                       ymax = y;
+               if (y < ymin)
+                       ymin = y;
+               if (a == endAngle)
+                       break;
+               if (a1 < 0)     /* clockwise */
+               {
+                       if (floor (a / 90.0) == floor (endAngle / 90.0))
+                               a = endAngle;
+                       else
+                               a = 90 * (floor (a/90.0) + 1);
+               }
+               else
+               {
+                       if (ceil (a / 90.0) == ceil (endAngle / 90.0))
+                               a = endAngle;
+                       else
+                               a = 90 * (ceil (a/90.0) - 1);
+               }
+       }
+       lx = ly = l;
+       if ((x1 - x0) + (y1 - y0) < 0)
+           lx = ly = -l;
+       if (h)
+       {
+           ly = 0.0;
+           lx = -lx;
+       }
+       else
+           lx = 0.0;
+       if (right)
+       {
+           right->center.x = x0;
+           right->center.y = y0;
+           right->clock.x = x0 - lx;
+           right->clock.y = y0 - ly;
+           right->counterClock.x = x0 + lx;
+           right->counterClock.y = y0 + ly;
+       }
+       if (left)
+       {
+           left->center.x = x1;
+           left->center.y = y1;
+           left->clock.x = x1 + lx;
+           left->clock.y = y1 + ly;
+           left->counterClock.x = x1 - lx;
+           left->counterClock.y = y1 - ly;
+       }
+       
+       x0 = xmin;
+       x1 = xmax;
+       y0 = ymin;
+       y1 = ymax;
+       if (ymin != y1) {
+               xmin = -l;
+               xmax = l;
+       } else {
+               ymin = -l;
+               ymax = l;
+       }
+       if (xmax != xmin && ymax != ymin) {
+               int     minx, maxx, miny, maxy;
+               xRectangle  rect;
+
+               minx = ICEIL (xmin + w) + tarc->x;
+               maxx = ICEIL (xmax + w) + tarc->x;
+               miny = ICEIL (ymin + h) + tarc->y;
+               maxy = ICEIL (ymax + h) + tarc->y;
+               rect.x = minx;
+               rect.y = miny;
+               rect.width = maxx - minx;
+               rect.height = maxy - miny;
+               (*pGC->ops->PolyFillRect) (pDraw, pGC, 1, &rect);
+       }
+}
+
+/*
+ * this computes the ellipse y value associated with the
+ * bottom of the tail.
+ */
+
+static void
+tailEllipseY (def, acc)
+       struct arc_def          *def;
+       struct accelerators     *acc;
+{
+       double          t;
+
+       acc->tail_y = 0.0;
+       if (def->w == def->h)
+           return;
+       t = def->l * def->w;
+       if (def->w > def->h) {
+           if (t < acc->h2)
+               return;
+       } else {
+           if (t > acc->h2)
+               return;
+       }
+       t = 2.0 * def->h * t;
+       t = (CUBED_ROOT_4 * acc->h2 - cbrt(t * t)) / acc->h2mw2;
+       if (t > 0.0)
+           acc->tail_y = def->h / CUBED_ROOT_2 * sqrt(t);
+}
+
+/*
+ * inverse functions -- compute edge coordinates
+ * from the ellipse
+ */
+
+static double
+outerXfromXY (x, y, def, acc)
+       double                  x, y;
+       struct arc_def          *def;
+       struct accelerators     *acc;
+{
+       return x + (x * acc->h2l) / sqrt (x*x * acc->h4 + y*y * acc->w4);
+}
+
+static double
+outerYfromXY (x, y, def, acc)
+       double          x, y;
+       struct arc_def          *def;
+       struct accelerators     *acc;
+{
+       return y + (y * acc->w2l) / sqrt (x*x * acc->h4 + y*y * acc->w4);
+}
+
+static double
+innerXfromXY (x, y, def, acc)
+       double                  x, y;
+       struct arc_def          *def;
+       struct accelerators     *acc;
+{
+       return x - (x * acc->h2l) / sqrt (x*x * acc->h4 + y*y * acc->w4);
+}
+
+static double
+innerYfromXY (x, y, def, acc)
+       double                  x, y;
+       struct arc_def          *def;
+       struct accelerators     *acc;
+{
+       return y - (y * acc->w2l) / sqrt (x*x * acc->h4 + y*y * acc->w4);
+}
+
+static double
+innerYfromY (y, def, acc)
+       double  y;
+       struct arc_def          *def;
+       struct accelerators     *acc;
+{
+       double  x;
+
+       x = (def->w / def->h) * sqrt (acc->h2 - y*y);
+
+       return y - (y * acc->w2l) / sqrt (x*x * acc->h4 + y*y * acc->w4);
+}
+
+static void
+computeLine (x1, y1, x2, y2, line)
+       double          x1, y1, x2, y2;
+       struct line     *line;
+{
+       if (y1 == y2)
+               line->valid = 0;
+       else {
+               line->m = (x1 - x2) / (y1 - y2);
+               line->b = x1  - y1 * line->m;
+               line->valid = 1;
+       }
+}
+
+/*
+ * compute various accelerators for an ellipse.  These
+ * are simply values that are used repeatedly in
+ * the computations
+ */
+
+static void
+computeAcc (tarc, lw, def, acc)
+       xArc                    *tarc;
+       int                     lw;
+       struct arc_def          *def;
+       struct accelerators     *acc;
+{
+       def->w = ((double) tarc->width) / 2.0;
+       def->h = ((double) tarc->height) / 2.0;
+       def->l = ((double) lw) / 2.0;
+       acc->h2 = def->h * def->h;
+       acc->w2 = def->w * def->w;
+       acc->h4 = acc->h2 * acc->h2;
+       acc->w4 = acc->w2 * acc->w2;
+       acc->h2l = acc->h2 * def->l;
+       acc->w2l = acc->w2 * def->l;
+       acc->h2mw2 = acc->h2 - acc->w2;
+       acc->fromIntX = (tarc->width & 1) ? 0.5 : 0.0;
+       acc->fromIntY = (tarc->height & 1) ? 0.5 : 0.0;
+       acc->xorg = tarc->x + (tarc->width >> 1);
+       acc->yorgu = tarc->y + (tarc->height >> 1);
+       acc->yorgl = acc->yorgu + (tarc->height & 1);
+       tailEllipseY (def, acc);
+}
+               
+/*
+ * compute y value bounds of various portions of the arc,
+ * the outer edge, the ellipse and the inner edge.
+ */
+
+static void
+computeBound (def, bound, acc, right, left)
+       struct arc_def          *def;
+       struct arc_bound        *bound;
+       struct accelerators     *acc;
+       miArcFacePtr            right, left;
+{
+       double          t;
+       double          innerTaily;
+       double          tail_y;
+       struct bound    innerx, outerx;
+       struct bound    ellipsex;
+
+       bound->ellipse.min = Dsin (def->a0) * def->h;
+       bound->ellipse.max = Dsin (def->a1) * def->h;
+       if (def->a0 == 45 && def->w == def->h)
+               ellipsex.min = bound->ellipse.min;
+       else
+               ellipsex.min = Dcos (def->a0) * def->w;
+       if (def->a1 == 45 && def->w == def->h)
+               ellipsex.max = bound->ellipse.max;
+       else
+               ellipsex.max = Dcos (def->a1) * def->w;
+       bound->outer.min = outerYfromXY (ellipsex.min, bound->ellipse.min, def, acc);
+       bound->outer.max = outerYfromXY (ellipsex.max, bound->ellipse.max, def, acc);
+       bound->inner.min = innerYfromXY (ellipsex.min, bound->ellipse.min, def, acc);
+       bound->inner.max = innerYfromXY (ellipsex.max, bound->ellipse.max, def, acc);
+
+       outerx.min = outerXfromXY (ellipsex.min, bound->ellipse.min, def, acc);
+       outerx.max = outerXfromXY (ellipsex.max, bound->ellipse.max, def, acc);
+       innerx.min = innerXfromXY (ellipsex.min, bound->ellipse.min, def, acc);
+       innerx.max = innerXfromXY (ellipsex.max, bound->ellipse.max, def, acc);
+       
+       /*
+        * save the line end points for the
+        * cap code to use.  Careful here, these are
+        * in cartesean coordinates (y increasing upwards)
+        * while the cap code uses inverted coordinates
+        * (y increasing downwards)
+        */
+
+       if (right) {
+               right->counterClock.y = bound->outer.min;
+               right->counterClock.x = outerx.min;
+               right->center.y = bound->ellipse.min;
+               right->center.x = ellipsex.min;
+               right->clock.y = bound->inner.min;
+               right->clock.x = innerx.min;
+       }
+
+       if (left) {
+               left->clock.y = bound->outer.max;
+               left->clock.x = outerx.max;
+               left->center.y = bound->ellipse.max;
+               left->center.x = ellipsex.max;
+               left->counterClock.y = bound->inner.max;
+               left->counterClock.x = innerx.max;
+       }
+
+       bound->left.min = bound->inner.max;
+       bound->left.max = bound->outer.max;
+       bound->right.min = bound->inner.min;
+       bound->right.max = bound->outer.min;
+
+       computeLine (innerx.min, bound->inner.min, outerx.min, bound->outer.min,
+                     &acc->right);
+       computeLine (innerx.max, bound->inner.max, outerx.max, bound->outer.max,
+                    &acc->left);
+
+       if (bound->inner.min > bound->inner.max) {
+               t = bound->inner.min;
+               bound->inner.min = bound->inner.max;
+               bound->inner.max = t;
+       }
+       tail_y = acc->tail_y;
+       if (tail_y > bound->ellipse.max)
+               tail_y = bound->ellipse.max;
+       else if (tail_y < bound->ellipse.min)
+               tail_y = bound->ellipse.min;
+       innerTaily = innerYfromY (tail_y, def, acc);
+       if (bound->inner.min > innerTaily)
+               bound->inner.min = innerTaily;
+       if (bound->inner.max < innerTaily)
+               bound->inner.max = innerTaily;
+       bound->inneri.min = ICEIL(bound->inner.min - acc->fromIntY);
+       bound->inneri.max = floor(bound->inner.max - acc->fromIntY);
+       bound->outeri.min = ICEIL(bound->outer.min - acc->fromIntY);
+       bound->outeri.max = floor(bound->outer.max - acc->fromIntY);
+}
+
+/*
+ * this section computes the x value of the span at y 
+ * intersected with the specified face of the ellipse.
+ *
+ * this is the min/max X value over the set of normal
+ * lines to the entire ellipse,  the equation of the
+ * normal lines is:
+ *
+ *     ellipse_x h^2                   h^2
+ * x = ------------ y + ellipse_x (1 - --- )
+ *     ellipse_y w^2                   w^2
+ *
+ * compute the derivative with-respect-to ellipse_y and solve
+ * for zero:
+ *    
+ *       (w^2 - h^2) ellipse_y^3 + h^4 y
+ * 0 = - ----------------------------------
+ *       h w ellipse_y^2 sqrt (h^2 - ellipse_y^2)
+ *
+ *             (   h^4 y     )
+ * ellipse_y = ( ----------  ) ^ (1/3)
+ *             ( (h^2 - w^2) )
+ *
+ * The other two solutions to the equation are imaginary.
+ *
+ * This gives the position on the ellipse which generates
+ * the normal with the largest/smallest x intersection point.
+ *
+ * Now compute the second derivative to check whether
+ * the intersection is a minimum or maximum:
+ *
+ *    h (y0^3 (w^2 - h^2) + h^2 y (3y0^2 - 2h^2))
+ * -  -------------------------------------------
+ *          w y0^3 (sqrt (h^2 - y^2)) ^ 3
+ *
+ * as we only care about the sign,
+ *
+ * - (y0^3 (w^2 - h^2) + h^2 y (3y0^2 - 2h^2))
+ *
+ * or (to use accelerators),
+ *
+ * y0^3 (h^2 - w^2) - h^2 y (3y0^2 - 2h^2) 
+ *
+ */
+
+/*
+ * computes the position on the ellipse whose normal line
+ * intersects the given scan line maximally
+ */
+
+static double
+hookEllipseY (scan_y, bound, acc, left)
+       double                  scan_y;
+       struct arc_bound        *bound;
+       struct accelerators     *acc;
+       int                     left;
+{
+       double  ret;
+
+       if (acc->h2mw2 == 0) {
+               if ( (scan_y > 0 && !left) || (scan_y < 0 && left) )
+                       return bound->ellipse.min;
+               return bound->ellipse.max;
+       }
+       ret = (acc->h4 * scan_y) / (acc->h2mw2);
+       if (ret >= 0)
+               return cbrt (ret);
+       else
+               return -cbrt (-ret);
+}
+
+/*
+ * computes the X value of the intersection of the
+ * given scan line with the right side of the lower hook
+ */
+
+static double
+hookX (scan_y, def, bound, acc, left)
+       double                  scan_y;
+       struct arc_def          *def;
+       struct arc_bound        *bound;
+       struct accelerators     *acc;
+       int                     left;
+{
+       double  ellipse_y, x;
+       double  maxMin;
+
+       if (def->w != def->h) {
+               ellipse_y = hookEllipseY (scan_y, bound, acc, left);
+               if (boundedLe (ellipse_y, bound->ellipse)) {
+                       /*
+                        * compute the value of the second
+                        * derivative
+                        */
+                       maxMin = ellipse_y*ellipse_y*ellipse_y * acc->h2mw2 -
+                        acc->h2 * scan_y * (3 * ellipse_y*ellipse_y - 2*acc->h2);
+                       if ((left && maxMin > 0) || (!left && maxMin < 0)) {
+                               if (ellipse_y == 0)
+                                       return def->w + left ? -def->l : def->l;
+                               x = (acc->h2 * scan_y - ellipse_y * acc->h2mw2) *
+                                       sqrt (acc->h2 - ellipse_y * ellipse_y) /
+                                       (def->h * def->w * ellipse_y);
+                               return x;
+                       }
+               }
+       }
+       if (left) {
+               if (acc->left.valid && boundedLe (scan_y, bound->left)) {
+                       x = intersectLine (scan_y, acc->left);
+               } else {
+                       if (acc->right.valid)
+                               x = intersectLine (scan_y, acc->right);
+                       else
+                               x = def->w - def->l;
+               }
+       } else {
+               if (acc->right.valid && boundedLe (scan_y, bound->right)) {
+                       x = intersectLine (scan_y, acc->right);
+               } else {
+                       if (acc->left.valid)
+                               x = intersectLine (scan_y, acc->left);
+                       else
+                               x = def->w - def->l;
+               }
+       }
+       return x;
+}
+
+/*
+ * generate the set of spans with
+ * the given y coordinate
+ */
+
+static void
+arcSpan (y, lx, lw, rx, rw, def, bounds, acc, mask)
+       int                     y;
+       int                     lx;
+       int                     lw;
+       int                     rx;
+       int                     rw;
+       struct arc_def          *def;
+       struct arc_bound        *bounds;
+       struct accelerators     *acc;
+       int                     mask;
+{
+       int linx, loutx, rinx, routx;
+       double x, altx;
+
+       if (boundedLe (y, bounds->inneri)) {
+           linx = -(lx + lw);
+           rinx = rx;
+       } else {
+           /*
+            * intersection with left face
+            */
+           x = hookX (y + acc->fromIntY, def, bounds, acc, 1);
+           if (acc->right.valid &&
+               boundedLe (y + acc->fromIntY, bounds->right))
+           {
+               altx = intersectLine (y + acc->fromIntY, acc->right);
+               if (altx < x)
+                   x = altx;
+           }
+           linx = -ICEIL(acc->fromIntX - x);
+           rinx = ICEIL(acc->fromIntX + x);
+       }
+       if (boundedLe (y, bounds->outeri)) {
+           loutx = -lx;
+           routx = rx + rw;
+       } else {
+           /*
+            * intersection with right face
+            */
+           x = hookX (y + acc->fromIntY, def, bounds, acc, 0);
+           if (acc->left.valid &&
+               boundedLe (y + acc->fromIntY, bounds->left))
+           {
+               altx = x;
+               x = intersectLine (y + acc->fromIntY, acc->left);
+               if (x < altx)
+                   x = altx;
+           }
+           loutx = -ICEIL(acc->fromIntX - x);
+           routx = ICEIL(acc->fromIntX + x);
+       }
+       if (routx > rinx) {
+           if (mask & 1)
+               newFinalSpan (acc->yorgu - y,
+                             acc->xorg + rinx, acc->xorg + routx);
+           if (mask & 8)
+               newFinalSpan (acc->yorgl + y,
+                             acc->xorg + rinx, acc->xorg + routx);
+       }
+       if (loutx > linx) {
+           if (mask & 2)
+               newFinalSpan (acc->yorgu - y,
+                             acc->xorg - loutx, acc->xorg - linx);
+           if (mask & 4)
+               newFinalSpan (acc->yorgl + y,
+                             acc->xorg - loutx, acc->xorg - linx);
+       }
+}
+
+static void
+arcSpan0 (lx, lw, rx, rw, def, bounds, acc, mask)
+       int                     lx;
+       int                     lw;
+       int                     rx;
+       int                     rw;
+       struct arc_def          *def;
+       struct arc_bound        *bounds;
+       struct accelerators     *acc;
+       int                     mask;
+{
+    double x;
+
+    if (boundedLe (0, bounds->inneri) &&
+       acc->left.valid && boundedLe (0, bounds->left) &&
+       acc->left.b > 0)
+    {
+       x = def->w - def->l;
+       if (acc->left.b < x)
+           x = acc->left.b;
+       lw = ICEIL(acc->fromIntX - x) - lx;
+       rw += rx;
+       rx = ICEIL(acc->fromIntX + x);
+       rw -= rx;
+    }
+    arcSpan (0, lx, lw, rx, rw, def, bounds, acc, mask);
+}
+
+static void
+tailSpan (y, lw, rw, def, bounds, acc, mask)
+       int                     y;
+       int                     lw;
+       int                     rw;
+       struct arc_def          *def;
+       struct arc_bound        *bounds;
+       struct accelerators     *acc;
+       int                     mask;
+{
+    double yy, xalt, x, lx, rx;
+    int n;
+
+    if (boundedLe(y, bounds->outeri))
+       arcSpan (y, 0, lw, -rw, rw, def, bounds, acc, mask);
+    else if (def->w != def->h) {
+       yy = y + acc->fromIntY;
+       x = tailX(yy, def, bounds, acc);
+       if (yy == 0.0 && x == -rw - acc->fromIntX)
+           return;
+       if (acc->right.valid && boundedLe (yy, bounds->right)) {
+           rx = x;
+           lx = -x;
+           xalt = intersectLine (yy, acc->right);
+           if (xalt >= -rw - acc->fromIntX && xalt <= rx)
+               rx = xalt;
+           n = ICEIL(acc->fromIntX + lx);
+           if (lw > n) {
+               if (mask & 2)
+                   newFinalSpan (acc->yorgu - y,
+                                 acc->xorg + n, acc->xorg + lw);
+               if (mask & 4)
+                   newFinalSpan (acc->yorgl + y,
+                                 acc->xorg + n, acc->xorg + lw);
+           }
+           n = ICEIL(acc->fromIntX + rx);
+           if (n > -rw) {
+               if (mask & 1)
+                   newFinalSpan (acc->yorgu - y,
+                                 acc->xorg - rw, acc->xorg + n);
+               if (mask & 8)
+                   newFinalSpan (acc->yorgl + y,
+                                 acc->xorg - rw, acc->xorg + n);
+           }
+       }
+       arcSpan (y,
+                ICEIL(acc->fromIntX - x), 0,
+                ICEIL(acc->fromIntX + x), 0,
+                def, bounds, acc, mask);
+    }
+}
+
+/*
+ * create whole arcs out of pieces.  This code is
+ * very bad.
+ */
+
+static struct finalSpan        **finalSpans = NULL;
+static int             finalMiny = 0, finalMaxy = -1;
+static int             finalSize = 0;
+
+static int             nspans = 0;     /* total spans, not just y coords */
+
+struct finalSpan {
+       struct finalSpan        *next;
+       int                     min, max;       /* x values */
+};
+
+static struct finalSpan    *freeFinalSpans, *tmpFinalSpan;
+
+# define allocFinalSpan()   (freeFinalSpans ?\
+                               ((tmpFinalSpan = freeFinalSpans), \
+                                (freeFinalSpans = freeFinalSpans->next), \
+                                (tmpFinalSpan->next = 0), \
+                                tmpFinalSpan) : \
+                            realAllocSpan ())
+
+# define SPAN_CHUNK_SIZE    128
+
+struct finalSpanChunk {
+       struct finalSpan        data[SPAN_CHUNK_SIZE];
+       struct finalSpanChunk   *next;
+};
+
+static struct finalSpanChunk   *chunks;
+
+struct finalSpan *
+realAllocSpan ()
+{
+       register struct finalSpanChunk  *newChunk;
+       register struct finalSpan       *span;
+       register int                    i;
+
+       newChunk = (struct finalSpanChunk *) xalloc (sizeof (struct finalSpanChunk));
+       if (!newChunk)
+               return (struct finalSpan *) NULL;
+       newChunk->next = chunks;
+       chunks = newChunk;
+       freeFinalSpans = span = newChunk->data + 1;
+       for (i = 1; i < SPAN_CHUNK_SIZE-1; i++) {
+               span->next = span+1;
+               span++;
+       }
+       span->next = 0;
+       span = newChunk->data;
+       span->next = 0;
+       return span;
+}
+
+static void
+disposeFinalSpans ()
+{
+       struct finalSpanChunk   *chunk, *next;
+
+       for (chunk = chunks; chunk; chunk = next) {
+               next = chunk->next;
+               xfree (chunk);
+       }
+       chunks = 0;
+       freeFinalSpans = 0;
+       xfree(finalSpans);
+       finalSpans = 0;
+}
+
+static void
+fillSpans (pDrawable, pGC)
+    DrawablePtr        pDrawable;
+    GCPtr      pGC;
+{
+       register struct finalSpan       *span;
+       register DDXPointPtr            xSpan;
+       register int                    *xWidth;
+       register int                    i;
+       register struct finalSpan       **f;
+       register int                    spany;
+       DDXPointPtr                     xSpans;
+       int                             *xWidths;
+
+       if (nspans == 0)
+               return;
+       xSpan = xSpans = (DDXPointPtr) ALLOCATE_LOCAL (nspans * sizeof (DDXPointRec));
+       xWidth = xWidths = (int *) ALLOCATE_LOCAL (nspans * sizeof (int));
+       if (xSpans && xWidths)
+       {
+           i = 0;
+           f = finalSpans;
+           for (spany = finalMiny; spany <= finalMaxy; spany++, f++) {
+                   for (span = *f; span; span=span->next) {
+                           if (span->max <= span->min)
+                                   continue;
+                           xSpan->x = span->min;
+                           xSpan->y = spany;
+                           ++xSpan;
+                           *xWidth++ = span->max - span->min;
+                           ++i;
+                   }
+           }
+           (*pGC->ops->FillSpans) (pDrawable, pGC, i, xSpans, xWidths, TRUE);
+       }
+       disposeFinalSpans ();
+       if (xSpans)
+           DEALLOCATE_LOCAL (xSpans);
+       if (xWidths)
+           DEALLOCATE_LOCAL (xWidths);
+       finalMiny = 0;
+       finalMaxy = -1;
+       finalSize = 0;
+       nspans = 0;
+}
+
+# define SPAN_REALLOC  100
+
+# define findSpan(y) ((finalMiny <= (y) && (y) <= finalMaxy) ? \
+                         &finalSpans[(y) - finalMiny] : \
+                         realFindSpan (y))
+
+static struct finalSpan **
+realFindSpan (y)
+    int y;
+{
+       struct finalSpan        **newSpans;
+       int                     newSize, newMiny, newMaxy;
+       int                     change;
+       int                     i;
+
+       if (y < finalMiny || y > finalMaxy) {
+               if (!finalSize) {
+                       finalMiny = y;
+                       finalMaxy = y - 1;
+               }
+               if (y < finalMiny)
+                       change = finalMiny - y;
+               else
+                       change = y - finalMaxy;
+               if (change >= SPAN_REALLOC)
+                       change += SPAN_REALLOC;
+               else
+                       change = SPAN_REALLOC;
+               newSize = finalSize + change;
+               newSpans = (struct finalSpan **) xalloc
+                                       (newSize * sizeof (struct finalSpan *));
+               if (!newSpans)
+                   return (struct finalSpan **)NULL;
+               newMiny = finalMiny;
+               newMaxy = finalMaxy;
+               if (y < finalMiny)
+                       newMiny = finalMiny - change;
+               else
+                       newMaxy = finalMaxy + change;
+               if (finalSpans) {
+                       memmove(((char *) newSpans) + (finalMiny-newMiny) * sizeof (struct finalSpan *),
+                               (char *) finalSpans,
+                              finalSize * sizeof (struct finalSpan *));
+                       xfree (finalSpans);
+               }
+               if ((i = finalMiny - newMiny) > 0)
+                       bzero ((char *)newSpans, i * sizeof (struct finalSpan *));
+               if ((i = newMaxy - finalMaxy) > 0)
+                       bzero ((char *)(newSpans + newSize - i),
+                              i * sizeof (struct finalSpan *));
+               finalSpans = newSpans;
+               finalMaxy = newMaxy;
+               finalMiny = newMiny;
+               finalSize = newSize;
+       }
+       return &finalSpans[y - finalMiny];
+}
+
+static void
+newFinalSpan (y, xmin, xmax)
+    int                y;
+    register int       xmin, xmax;
+{
+       register struct finalSpan       *x;
+       register struct finalSpan       **f;
+       struct finalSpan                *oldx;
+       struct finalSpan                *prev;
+
+       f = findSpan (y);
+       if (!f)
+           return;
+       oldx = 0;
+       for (;;) {
+               prev = 0;
+               for (x = *f; x; x=x->next) {
+                       if (x == oldx) {
+                               prev = x;
+                               continue;
+                       }
+                       if (x->min <= xmax && xmin <= x->max) {
+                               if (oldx) {
+                                       oldx->min = min (x->min, xmin);
+                                       oldx->max = max (x->max, xmax);
+                                       if (prev)
+                                               prev->next = x->next;
+                                       else
+                                               *f = x->next;
+                                       --nspans;
+                               } else {
+                                       x->min = min (x->min, xmin);
+                                       x->max = max (x->max, xmax);
+                                       oldx = x;
+                               }
+                               xmin = oldx->min;
+                               xmax = oldx->max;
+                               break;
+                       }
+                       prev = x;
+               }
+               if (!x)
+                       break;
+       }
+       if (!oldx) {
+               x = allocFinalSpan ();
+               if (x)
+               {
+                   x->min = xmin;
+                   x->max = xmax;
+                   x->next = *f;
+                   *f = x;
+                   ++nspans;
+               }
+       }
+}
+
+static void
+mirrorSppPoint (quadrant, sppPoint)
+       int             quadrant;
+       SppPointPtr     sppPoint;
+{
+       switch (quadrant) {
+       case 0:
+               break;
+       case 1:
+               sppPoint->x = -sppPoint->x;
+               break;
+       case 2:
+               sppPoint->x = -sppPoint->x;
+               sppPoint->y = -sppPoint->y;
+               break;
+       case 3:
+               sppPoint->y = -sppPoint->y;
+               break;
+       }
+       /*
+        * and translate to X coordinate system
+        */
+       sppPoint->y = -sppPoint->y;
+}
+
+/*
+ * split an arc into pieces which are scan-converted
+ * in the first-quadrant and mirrored into position.
+ * This is necessary as the scan-conversion code can
+ * only deal with arcs completely contained in the
+ * first quadrant.
+ */
+
+static void
+drawArc (tarc, l, a0, a1, right, left)
+       xArc *tarc;
+       int     l, a0, a1;
+       miArcFacePtr    right, left;    /* save end line points */
+{
+       struct arc_def          def;
+       struct accelerators     acc;
+       int                     startq, endq, curq;
+       int                     rightq, leftq, righta, lefta;
+       miArcFacePtr            passRight, passLeft;
+       int                     q0, q1, mask;
+       struct band {
+               int     a0, a1;
+               int     mask;
+       }       band[5], sweep[20];
+       int                     bandno, sweepno;
+       int                     i, j;
+       int                     flipRight = 0, flipLeft = 0;                    
+       int                     copyEnd = 0;
+       miArcSpanData           *spdata;
+       Bool                    mustFree;
+
+       spdata = miComputeWideEllipse(l, tarc, &mustFree);
+       if (!spdata)
+           return;
+
+       if (a1 < a0)
+               a1 += 360 * 64;
+       startq = a0 / (90 * 64);
+       if (a0 == a1)
+           endq = startq;
+       else
+           endq = (a1-1) / (90 * 64);
+       bandno = 0;
+       curq = startq;
+       rightq = -1;
+       for (;;) {
+               switch (curq) {
+               case 0:
+                       if (a0 > 90 * 64)
+                               q0 = 0;
+                       else
+                               q0 = a0;
+                       if (a1 < 360 * 64)
+                               q1 = min (a1, 90 * 64);
+                       else
+                               q1 = 90 * 64;
+                       if (curq == startq && a0 == q0 && rightq < 0) {
+                               righta = q0;
+                               rightq = curq;
+                       }
+                       if (curq == endq && a1 == q1) {
+                               lefta = q1;
+                               leftq = curq;
+                       }
+                       break;
+               case 1:
+                       if (a1 < 90 * 64)
+                               q0 = 0;
+                       else
+                               q0 = 180 * 64 - min (a1, 180 * 64);
+                       if (a0 > 180 * 64)
+                               q1 = 90 * 64;
+                       else
+                               q1 = 180 * 64 - max (a0, 90 * 64);
+                       if (curq == startq && 180 * 64 - a0 == q1) {
+                               righta = q1;
+                               rightq = curq;
+                       }
+                       if (curq == endq && 180 * 64 - a1 == q0) {
+                               lefta = q0;
+                               leftq = curq;
+                       }
+                       break;
+               case 2:
+                       if (a0 > 270 * 64)
+                               q0 = 0;
+                       else
+                               q0 = max (a0, 180 * 64) - 180 * 64;
+                       if (a1 < 180 * 64)
+                               q1 = 90 * 64;
+                       else
+                               q1 = min (a1, 270 * 64) - 180 * 64;
+                       if (curq == startq && a0 - 180*64 == q0) {
+                               righta = q0;
+                               rightq = curq;
+                       }
+                       if (curq == endq && a1 - 180 * 64 == q1) {
+                               lefta = q1;
+                               leftq = curq;
+                       }
+                       break;
+               case 3:
+                       if (a1 < 270 * 64)
+                               q0 = 0;
+                       else
+                               q0 = 360 * 64 - min (a1, 360 * 64);
+                       q1 = 360 * 64 - max (a0, 270 * 64);
+                       if (curq == startq && 360 * 64 - a0 == q1) {
+                               righta = q1;
+                               rightq = curq;
+                       }
+                       if (curq == endq && 360 * 64 - a1 == q0) {
+                               lefta = q0;
+                               leftq = curq;
+                       }
+                       break;
+               }
+               band[bandno].a0 = q0;
+               band[bandno].a1 = q1;
+               band[bandno].mask = 1 << curq;
+               bandno++;
+               if (curq == endq)
+                       break;
+               curq++;
+               if (curq == 4) {
+                       a0 = 0;
+                       a1 -= 360 * 64;
+                       curq = 0;
+                       endq -= 4;
+               }
+       }
+       sweepno = 0;
+       for (;;) {
+               q0 = 90 * 64;
+               mask = 0;
+               /*
+                * find left-most point
+                */
+               for (i = 0; i < bandno; i++)
+                       if (band[i].a0 <= q0) {
+                               q0 = band[i].a0;
+                               q1 = band[i].a1;
+                               mask = band[i].mask;
+                       }
+               if (!mask)
+                       break;
+               /*
+                * locate next point of change
+                */
+               for (i = 0; i < bandno; i++)
+                       if (!(mask & band[i].mask)) {
+                               if (band[i].a0 == q0) {
+                                       if (band[i].a1 < q1)
+                                               q1 = band[i].a1;
+                                       mask |= band[i].mask;
+                               } else if (band[i].a0 < q1)
+                                       q1 = band[i].a0;
+                       }
+               /*
+                * create a new sweep
+                */
+               sweep[sweepno].a0 = q0;
+               sweep[sweepno].a1 = q1;
+               sweep[sweepno].mask = mask;
+               sweepno++;
+               /*
+                * subtract the sweep from the affected bands
+                */
+               for (i = 0; i < bandno; i++)
+                       if (band[i].a0 == q0) {
+                               band[i].a0 = q1;
+                               /*
+                                * check if this band is empty
+                                */
+                               if (band[i].a0 == band[i].a1)
+                                       band[i].a1 = band[i].a0 = 90 * 64 + 1;
+                       }
+       }
+       computeAcc (tarc, l, &def, &acc);
+       for (j = 0; j < sweepno; j++) {
+               mask = sweep[j].mask;
+               passRight = passLeft = 0;
+               if (mask & (1 << rightq)) {
+                       if (sweep[j].a0 == righta)
+                               passRight = right;
+                       else if (sweep[j].a1 == righta) {
+                               passLeft = right;
+                               flipRight = 1;
+                       }
+               }
+               if (mask & (1 << leftq)) {
+                       if (sweep[j].a1 == lefta)
+                       {
+                               if (passLeft)
+                                       copyEnd = 1;
+                               passLeft = left;
+                       }
+                       else if (sweep[j].a0 == lefta) {
+                               if (passRight)
+                                       copyEnd = 1;
+                               passRight = left;
+                               flipLeft = 1;
+                       }
+               }
+               drawQuadrant (&def, &acc, sweep[j].a0, sweep[j].a1, mask, 
+                             passRight, passLeft, spdata);
+       }
+       /*
+        * when copyEnd is set, both ends of the arc were computed
+        * at the same time; drawQuadrant only takes one end though,
+        * so the left end will be the only one holding the data.  Copy
+        * it from there.
+        */
+       if (copyEnd)
+               *right = *left;
+       /*
+        * mirror the coordinates generated for the
+        * faces of the arc
+        */
+       if (right) {
+               mirrorSppPoint (rightq, &right->clock);
+               mirrorSppPoint (rightq, &right->center);
+               mirrorSppPoint (rightq, &right->counterClock);
+               if (flipRight) {
+                       SppPointRec     temp;
+
+                       temp = right->clock;
+                       right->clock = right->counterClock;
+                       right->counterClock = temp;
+               }
+       }
+       if (left) {
+               mirrorSppPoint (leftq,  &left->counterClock);
+               mirrorSppPoint (leftq,  &left->center);
+               mirrorSppPoint (leftq,  &left->clock);
+               if (flipLeft) {
+                       SppPointRec     temp;
+
+                       temp = left->clock;
+                       left->clock = left->counterClock;
+                       left->counterClock = temp;
+               }
+       }
+       if (mustFree)
+           xfree(spdata);
+}
+
+static void
+drawQuadrant (def, acc, a0, a1, mask, right, left, spdata)
+       struct arc_def          *def;
+       struct accelerators     *acc;
+       int                     a0, a1;
+       int                     mask;
+       miArcFacePtr            right, left;
+       miArcSpanData           *spdata;
+{
+       struct arc_bound        bound;
+       double                  yy, x, xalt;
+       int                     y, miny, maxy;
+       int                     n;
+       miArcSpan               *span;
+
+       def->a0 = ((double) a0) / 64.0;
+       def->a1 = ((double) a1) / 64.0;
+       computeBound (def, &bound, acc, right, left);
+       yy = bound.inner.min;
+       if (bound.outer.min < yy)
+           yy = bound.outer.min;
+       miny = ICEIL(yy - acc->fromIntY);
+       yy = bound.inner.max;
+       if (bound.outer.max > yy)
+           yy = bound.outer.max;
+       maxy = floor(yy - acc->fromIntY);
+       y = spdata->k;
+       span = spdata->spans;
+       if (spdata->top)
+       {
+           if (a1 == 90 * 64 && (mask & 1))
+               newFinalSpan (acc->yorgu - y - 1, acc->xorg, acc->xorg + 1);
+           span++;
+       }
+       for (n = spdata->count1; --n >= 0; )
+       {
+           if (y < miny)
+               return;
+           if (y <= maxy) {
+               arcSpan (y,
+                        span->lx, -span->lx, 0, span->lx + span->lw,
+                        def, &bound, acc, mask);
+               if (span->rw + span->rx)
+                   tailSpan (y, -span->rw, -span->rx, def, &bound, acc, mask);
+           }
+           y--;
+           span++;
+       }
+       if (y < miny)
+           return;
+       if (spdata->hole)
+       {
+           if (y <= maxy)
+               arcSpan (y, 0, 0, 0, 1, def, &bound, acc, mask & 0xc);
+       }
+       for (n = spdata->count2; --n >= 0; )
+       {
+           if (y < miny)
+               return;
+           if (y <= maxy)
+               arcSpan (y, span->lx, span->lw, span->rx, span->rw,
+                        def, &bound, acc, mask);
+           y--;
+           span++;
+       }
+       if (spdata->bot && miny <= y && y <= maxy)
+       {
+           n = mask;
+           if (y == miny)
+               n &= 0xc;
+           if (span->rw <= 0) {
+               arcSpan0 (span->lx, -span->lx, 0, span->lx + span->lw,
+                         def, &bound, acc, n);
+               if (span->rw + span->rx)
+                   tailSpan (y, -span->rw, -span->rx, def, &bound, acc, n);
+           }
+           else
+               arcSpan0 (span->lx, span->lw, span->rx, span->rw,
+                         def, &bound, acc, n);
+           y--;
+       }
+       while (y >= miny) {
+           yy = y + acc->fromIntY;
+           if (def->w == def->h) {
+               xalt = def->w - def->l;
+               x = -sqrt(xalt * xalt - yy * yy);
+           } else {
+               x = tailX(yy, def, &bound, acc);
+               if (acc->left.valid && boundedLe (yy, bound.left)) {
+                   xalt = intersectLine (yy, acc->left);
+                   if (xalt < x)
+                       x = xalt;
+               }
+               if (acc->right.valid && boundedLe (yy, bound.right)) {
+                   xalt = intersectLine (yy, acc->right);
+                   if (xalt < x)
+                       x = xalt;
+               }
+           }
+           arcSpan (y,
+                    ICEIL(acc->fromIntX - x), 0,
+                    ICEIL(acc->fromIntX + x), 0,
+                    def, &bound, acc, mask);
+           y--;
+       }
+}