3600 lines
		
	
	
		
			104 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			3600 lines
		
	
	
		
			104 KiB
		
	
	
	
		
			C
		
	
	
	
| /***********************************************************
 | |
| 
 | |
| Copyright 1987, 1998  The Open Group
 | |
| 
 | |
| Permission to use, copy, modify, distribute, and sell this software and its
 | |
| documentation for any purpose is hereby granted without fee, provided that
 | |
| the above copyright notice appear in all copies and that both that
 | |
| copyright notice and this permission notice appear in supporting
 | |
| documentation.
 | |
| 
 | |
| 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
 | |
| OPEN GROUP 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 Open Group 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 Open Group.
 | |
| 
 | |
| 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.
 | |
| 
 | |
| ******************************************************************/
 | |
| /* Author: Keith Packard and Bob Scheifler */
 | |
| /* Warning: this code is toxic, do not dally very long here. */
 | |
| 
 | |
| #include <dix-config.h>
 | |
| 
 | |
| #include <math.h>
 | |
| #include <X11/X.h>
 | |
| #include <X11/Xprotostr.h>
 | |
| #include <X11/Xfuncproto.h>
 | |
| 
 | |
| #include "mi/mi_priv.h"
 | |
| 
 | |
| #include "misc.h"
 | |
| #include "gcstruct.h"
 | |
| #include "scrnintstr.h"
 | |
| #include "pixmapstr.h"
 | |
| #include "windowstr.h"
 | |
| #include "mifpoly.h"
 | |
| #include "mifillarc.h"
 | |
| 
 | |
| #define EPSILON	0.000001
 | |
| #define ISEQUAL(a,b) (fabs((a) - (b)) <= EPSILON)
 | |
| #define UNEQUAL(a,b) (fabs((a) - (b)) > EPSILON)
 | |
| #define PTISEQUAL(a,b) (ISEQUAL(a.x,b.x) && ISEQUAL(a.y,b.y))
 | |
| #define SQSECANT 108.856472512142   /* 1/sin^2(11/2) - for 11o miter cutoff */
 | |
| 
 | |
| /* Point with sub-pixel positioning. */
 | |
| typedef struct _SppPoint {
 | |
|     double x, y;
 | |
| } SppPointRec, *SppPointPtr;
 | |
| 
 | |
| typedef struct _SppArc {
 | |
|     double x, y, width, height;
 | |
|     double angle1, angle2;
 | |
| } SppArcRec, *SppArcPtr;
 | |
| 
 | |
| static double miDsin(double a);
 | |
| static double miDcos(double a);
 | |
| static double miDasin(double v);
 | |
| static double miDatan2(double dy, double dx);
 | |
| 
 | |
| #ifndef HAVE_CBRT
 | |
| static double
 | |
| cbrt(double x)
 | |
| {
 | |
|     if (x > 0.0)
 | |
|         return pow(x, 1.0 / 3.0);
 | |
|     else
 | |
|         return -pow(-x, 1.0 / 3.0);
 | |
| }
 | |
| #endif
 | |
| 
 | |
| /*
 | |
|  * some interesting semantic 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".
 | |
|  *
 | |
|  */
 | |
| 
 | |
| 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;
 | |
| 
 | |
| typedef struct {
 | |
|     short lx, lw, rx, rw;
 | |
| } miArcSpan;
 | |
| 
 | |
| typedef struct {
 | |
|     miArcSpan *spans;
 | |
|     int count1, count2, k;
 | |
|     char top, bot, hole;
 | |
| } miArcSpanData;
 | |
| 
 | |
| static void fillSpans(DrawablePtr pDrawable, GCPtr pGC);
 | |
| static void newFinalSpan(int y, int xmin, int xmax);
 | |
| static miArcSpanData *drawArc(xArc * tarc, int l, int a0, int a1,
 | |
|                               miArcFacePtr right, miArcFacePtr left,
 | |
|                               miArcSpanData *spdata);
 | |
| static void drawZeroArc(DrawablePtr pDraw, GCPtr pGC, xArc * tarc, int lw,
 | |
|                         miArcFacePtr left, miArcFacePtr right);
 | |
| static void miArcJoin(DrawablePtr pDraw, GCPtr pGC, miArcFacePtr pLeft,
 | |
|                       miArcFacePtr pRight, int xOrgLeft, int yOrgLeft,
 | |
|                       double xFtransLeft, double yFtransLeft,
 | |
|                       int xOrgRight, int yOrgRight,
 | |
|                       double xFtransRight, double yFtransRight);
 | |
| static void miArcCap(DrawablePtr pDraw, GCPtr pGC, miArcFacePtr pFace,
 | |
|                      int end, int xOrg, int yOrg, double xFtrans,
 | |
|                      double yFtrans);
 | |
| static void miRoundCap(DrawablePtr pDraw, GCPtr pGC, SppPointRec pCenter,
 | |
|                        SppPointRec pEnd, SppPointRec pCorner,
 | |
|                        SppPointRec pOtherCorner, int fLineEnd,
 | |
|                        int xOrg, int yOrg, double xFtrans, double yFtrans);
 | |
| static void miFreeArcs(miPolyArcPtr arcs, GCPtr pGC);
 | |
| static miPolyArcPtr miComputeArcs(xArc * parcs, int narcs, GCPtr pGC);
 | |
| static int miGetArcPts(SppArcPtr parc, int cpt, SppPointPtr * ppPts);
 | |
| 
 | |
| #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 miArcSpanData *
 | |
| miArcSegment(DrawablePtr pDraw, GCPtr pGC, xArc tarc, miArcFacePtr right,
 | |
|              miArcFacePtr left, miArcSpanData *spdata)
 | |
| {
 | |
|     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 spdata;
 | |
|     }
 | |
| 
 | |
|     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;
 | |
|     }
 | |
| 
 | |
|     return drawArc(&tarc, l, startAngle, endAngle, right, left, spdata);
 | |
| }
 | |
| 
 | |
| /*
 | |
| 
 | |
| 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.
 | |
| 
 | |
| */
 | |
| 
 | |
| static void drawQuadrant(struct arc_def *def, struct accelerators *acc,
 | |
|                          int a0, int a1, int mask, miArcFacePtr right,
 | |
|                          miArcFacePtr left, miArcSpanData * spdata);
 | |
| 
 | |
| static void
 | |
| miComputeCircleSpans(int lw, xArc * parc, miArcSpanData * spdata)
 | |
| {
 | |
|     miArcSpan *span;
 | |
|     int doinner;
 | |
|     int x, y, e;
 | |
|     int xk, yk, xm, ym, dx, dy;
 | |
|     int slw, inslw;
 | |
|     int inx = 0, iny, ine = 0;
 | |
|     int inxk = 0, inyk = 0, inxm = 0, inym = 0;
 | |
| 
 | |
|     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(int lw, xArc * parc, miArcSpanData * spdata)
 | |
| {
 | |
|     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 = 0.0, 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(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(int lw, xArc * parc)
 | |
| {
 | |
|     int k;
 | |
| 
 | |
|     if (!lw)
 | |
|         lw = 1;
 | |
|     k = (parc->height >> 1) + ((lw - 1) >> 1);
 | |
|     miArcSpanData *spdata = calloc(1, sizeof(miArcSpanData) + sizeof(miArcSpan) * (k + 2));
 | |
|     if (!spdata)
 | |
|         return NULL;
 | |
|     spdata->spans = (miArcSpan *) (spdata + 1);
 | |
|     spdata->k = k;
 | |
|     spdata->top = !(lw & 1) && !(parc->width & 1);
 | |
|     spdata->bot = !(parc->height & 1);
 | |
|     if (parc->width == parc->height)
 | |
|         miComputeCircleSpans(lw, parc, spdata);
 | |
|     else
 | |
|         miComputeEllipseSpans(lw, parc, spdata);
 | |
|     return spdata;
 | |
| }
 | |
| 
 | |
| static void
 | |
| miFillWideEllipse(DrawablePtr pDraw, GCPtr pGC, xArc * parc)
 | |
| {
 | |
|     DDXPointPtr points;
 | |
|     DDXPointPtr pts;
 | |
|     int *wids;
 | |
|     miArcSpanData *spdata;
 | |
|     miArcSpan *span;
 | |
|     int xorg, yorgu, yorgl;
 | |
|     int n;
 | |
| 
 | |
|     yorgu = parc->height + pGC->lineWidth;
 | |
|     n = (sizeof(int) * 2) * yorgu;
 | |
|     int *widths = calloc(1, n + (sizeof(DDXPointRec) * 2) * yorgu);
 | |
|     if (!widths)
 | |
|         return;
 | |
|     points = (DDXPointPtr) ((char *) widths + n);
 | |
|     spdata = miComputeWideEllipse((int) pGC->lineWidth, parc);
 | |
|     if (!spdata) {
 | |
|         free(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;
 | |
|         }
 | |
|     }
 | |
|     free(spdata);
 | |
|     (*pGC->ops->FillSpans) (pDraw, pGC, pts - points, points, widths, FALSE);
 | |
| 
 | |
|     free(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
 | |
| miWideArc(DrawablePtr pDraw, GCPtr pGC, int narcs, xArc * parcs)
 | |
| {
 | |
|     int i;
 | |
|     xArc *parc;
 | |
|     int xMin, xMax, yMin, yMax;
 | |
|     int pixmapWidth = 0, pixmapHeight = 0;
 | |
|     int xOrg = 0, yOrg = 0;
 | |
|     int width = pGC->lineWidth;
 | |
|     Bool fTricky;
 | |
|     DrawablePtr pDrawTo;
 | |
|     CARD32 fg, bg;
 | |
|     GCPtr pGCTo;
 | |
|     miPolyArcPtr polyArcs;
 | |
|     int cap[2], join[2];
 | |
|     int iphase;
 | |
|     int halfWidth;
 | |
| 
 | |
|     if (width == 0 && pGC->lineStyle == LineSolid) {
 | |
|         for (i = narcs, parc = parcs; --i >= 0; parc++) {
 | |
|             miArcSpanData *spdata;
 | |
|             spdata = miArcSegment(pDraw, pGC, *parc, NULL, NULL, NULL);
 | |
|             free(spdata);
 | |
|         }
 | |
|         fillSpans(pDraw, pGC);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     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;
 | |
|         {
 | |
|             ChangeGCVal gcvals[6];
 | |
| 
 | |
|             gcvals[0].val = GXcopy;
 | |
|             gcvals[1].val = 1;
 | |
|             gcvals[2].val = 0;
 | |
|             gcvals[3].val = pGC->lineWidth;
 | |
|             gcvals[4].val = pGC->capStyle;
 | |
|             gcvals[5].val = pGC->joinStyle;
 | |
|             ChangeGC(NullClient, pGCTo, GCFunction |
 | |
|                      GCForeground | GCBackground | GCLineWidth |
 | |
|                      GCCapStyle | GCJoinStyle, gcvals);
 | |
|         }
 | |
| 
 | |
|         /* allocate a bitmap of the appropriate size, and validate it */
 | |
|         pDrawTo = (DrawablePtr) (*pDraw->pScreen->CreatePixmap)
 | |
|             (pDraw->pScreen, pixmapWidth, pixmapHeight, 1,
 | |
|              CREATE_PIXMAP_USAGE_SCRATCH);
 | |
|         if (!pDrawTo) {
 | |
|             FreeScratchGC(pGCTo);
 | |
|             return;
 | |
|         }
 | |
|         ValidateGC(pDrawTo, pGCTo);
 | |
|         miClearDrawable(pDrawTo, pGCTo);
 | |
|     }
 | |
| 
 | |
|     fg = pGC->fgPixel;
 | |
|     bg = pGC->bgPixel;
 | |
| 
 | |
|     /* the protocol sez these don't cause color changes */
 | |
|     if ((pGC->fillStyle == FillTiled) ||
 | |
|         (pGC->fillStyle == FillOpaqueStippled))
 | |
|         bg = fg;
 | |
| 
 | |
|     polyArcs = miComputeArcs(parcs, narcs, pGC);
 | |
|     if (!polyArcs)
 | |
|         goto out;
 | |
| 
 | |
|     cap[0] = cap[1] = 0;
 | |
|     join[0] = join[1] = 0;
 | |
|     for (iphase = (pGC->lineStyle == LineDoubleDash); iphase >= 0; iphase--) {
 | |
|         miArcSpanData *spdata = NULL;
 | |
|         xArc lastArc;
 | |
|         ChangeGCVal gcval;
 | |
| 
 | |
|         if (iphase == 1) {
 | |
|             gcval.val = bg;
 | |
|             ChangeGC(NullClient, pGC, GCForeground, &gcval);
 | |
|             ValidateGC(pDraw, pGC);
 | |
|         }
 | |
|         else if (pGC->lineStyle == LineDoubleDash) {
 | |
|             gcval.val = fg;
 | |
|             ChangeGC(NullClient, pGC, GCForeground, &gcval);
 | |
|             ValidateGC(pDraw, pGC);
 | |
|         }
 | |
|         for (i = 0; i < polyArcs[iphase].narcs; i++) {
 | |
|             miArcDataPtr arcData;
 | |
| 
 | |
|             arcData = &polyArcs[iphase].arcs[i];
 | |
|             if (spdata) {
 | |
|                 if (lastArc.width != arcData->arc.width ||
 | |
|                     lastArc.height != arcData->arc.height) {
 | |
|                     free(spdata);
 | |
|                     spdata = NULL;
 | |
|                 }
 | |
|             }
 | |
|             memcpy(&lastArc, &arcData->arc, sizeof(xArc));
 | |
|             spdata = miArcSegment(pDrawTo, pGCTo, arcData->arc,
 | |
|                                   &arcData->bounds[RIGHT_END],
 | |
|                                   &arcData->bounds[LEFT_END], spdata);
 | |
|             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);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         free(spdata);
 | |
|         spdata = NULL;
 | |
|     }
 | |
|     miFreeArcs(polyArcs, pGC);
 | |
| 
 | |
| out:
 | |
|     if (fTricky) {
 | |
|         dixDestroyPixmap((PixmapPtr) pDrawTo, 0);
 | |
|         FreeScratchGC(pGCTo);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* Find the index of the point with the smallest y.also return the
 | |
|  * smallest and largest y */
 | |
| static int
 | |
| GetFPolyYBounds(SppPointPtr pts, int n, double yFtrans, int *by, int *ty)
 | |
| {
 | |
|     SppPointPtr ptMin;
 | |
|     double ymin, ymax;
 | |
|     SppPointPtr ptsStart = pts;
 | |
| 
 | |
|     ptMin = pts;
 | |
|     ymin = ymax = (pts++)->y;
 | |
| 
 | |
|     while (--n > 0) {
 | |
|         if (pts->y < ymin) {
 | |
|             ptMin = pts;
 | |
|             ymin = pts->y;
 | |
|         }
 | |
|         if (pts->y > ymax)
 | |
|             ymax = pts->y;
 | |
| 
 | |
|         pts++;
 | |
|     }
 | |
| 
 | |
|     *by = ICEIL(ymin + yFtrans);
 | |
|     *ty = ICEIL(ymax + yFtrans - 1);
 | |
|     return ptMin - ptsStart;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  *	miFillSppPoly written by Todd Newman; April. 1987.
 | |
|  *
 | |
|  *	Fill a convex polygon.  If the given polygon
 | |
|  *	is not convex, then the result is undefined.
 | |
|  *	The algorithm is to order the edges from smallest
 | |
|  *	y to largest by partitioning the array into a left
 | |
|  *	edge list and a right edge list.  The algorithm used
 | |
|  *	to traverse each edge is digital differencing analyzer
 | |
|  *	line algorithm with y as the major axis. There's some funny linear
 | |
|  *	interpolation involved because of the subpixel postioning.
 | |
|  */
 | |
| static void
 | |
| miFillSppPoly(DrawablePtr dst, GCPtr pgc, int count,    /* number of points */
 | |
|               SppPointPtr ptsIn,        /* the points */
 | |
|               int xTrans, int yTrans,   /* Translate each point by this */
 | |
|               double xFtrans, double yFtrans    /* translate before conversion
 | |
|                                                    by this amount.  This provides
 | |
|                                                    a mechanism to match rounding
 | |
|                                                    errors with any shape that must
 | |
|                                                    meet the polygon exactly.
 | |
|                                                  */
 | |
|     )
 | |
| {
 | |
|     double xl = 0.0, xr = 0.0,  /* x vals of left and right edges */
 | |
|         ml = 0.0,               /* left edge slope */
 | |
|         mr = 0.0,               /* right edge slope */
 | |
|         dy,                     /* delta y */
 | |
|         i;                      /* loop counter */
 | |
|     int y,                      /* current scanline */
 | |
|      j, imin,                   /* index of vertex with smallest y */
 | |
|      ymin,                      /* y-extents of polygon */
 | |
|      ymax, *width, *FirstWidth, /* output buffer */
 | |
|     *Marked;                    /* set if this vertex has been used */
 | |
|     int left, right,            /* indices to first endpoints */
 | |
|      nextleft, nextright;       /* indices to second endpoints */
 | |
|     DDXPointPtr ptsOut, FirstPoint;     /* output buffer */
 | |
| 
 | |
|     if (pgc->miTranslate) {
 | |
|         xTrans += dst->x;
 | |
|         yTrans += dst->y;
 | |
|     }
 | |
| 
 | |
|     imin = GetFPolyYBounds(ptsIn, count, yFtrans, &ymin, &ymax);
 | |
| 
 | |
|     y = ymax - ymin + 1;
 | |
|     if ((count < 3) || (y <= 0))
 | |
|         return;
 | |
|     ptsOut = FirstPoint = calloc(y, sizeof(DDXPointRec));
 | |
|     width = FirstWidth = calloc(y, sizeof(int));
 | |
|     Marked = calloc(count, sizeof(int));
 | |
| 
 | |
|     if (!ptsOut || !width || !Marked) {
 | |
|         free(Marked);
 | |
|         free(width);
 | |
|         free(ptsOut);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     for (j = 0; j < count; j++)
 | |
|         Marked[j] = 0;
 | |
|     nextleft = nextright = imin;
 | |
|     Marked[imin] = -1;
 | |
|     y = ICEIL(ptsIn[nextleft].y + yFtrans);
 | |
| 
 | |
|     /*
 | |
|      *  loop through all edges of the polygon
 | |
|      */
 | |
|     do {
 | |
|         /* add a left edge if we need to */
 | |
|         if ((y > (ptsIn[nextleft].y + yFtrans) ||
 | |
|              ISEQUAL(y, ptsIn[nextleft].y + yFtrans)) &&
 | |
|             Marked[nextleft] != 1) {
 | |
|             Marked[nextleft]++;
 | |
|             left = nextleft++;
 | |
| 
 | |
|             /* find the next edge, considering the end conditions */
 | |
|             if (nextleft >= count)
 | |
|                 nextleft = 0;
 | |
| 
 | |
|             /* now compute the starting point and slope */
 | |
|             dy = ptsIn[nextleft].y - ptsIn[left].y;
 | |
|             if (dy != 0.0) {
 | |
|                 ml = (ptsIn[nextleft].x - ptsIn[left].x) / dy;
 | |
|                 dy = y - (ptsIn[left].y + yFtrans);
 | |
|                 xl = (ptsIn[left].x + xFtrans) + ml * max(dy, 0);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /* add a right edge if we need to */
 | |
|         if ((y > ptsIn[nextright].y + yFtrans) ||
 | |
|             (ISEQUAL(y, ptsIn[nextright].y + yFtrans)
 | |
|              && Marked[nextright] != 1)) {
 | |
|             Marked[nextright]++;
 | |
|             right = nextright--;
 | |
| 
 | |
|             /* find the next edge, considering the end conditions */
 | |
|             if (nextright < 0)
 | |
|                 nextright = count - 1;
 | |
| 
 | |
|             /* now compute the starting point and slope */
 | |
|             dy = ptsIn[nextright].y - ptsIn[right].y;
 | |
|             if (dy != 0.0) {
 | |
|                 mr = (ptsIn[nextright].x - ptsIn[right].x) / dy;
 | |
|                 dy = y - (ptsIn[right].y + yFtrans);
 | |
|                 xr = (ptsIn[right].x + xFtrans) + mr * max(dy, 0);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /*
 | |
|          *  generate scans to fill while we still have
 | |
|          *  a right edge as well as a left edge.
 | |
|          */
 | |
|         i = (min(ptsIn[nextleft].y, ptsIn[nextright].y) + yFtrans) - y;
 | |
| 
 | |
|         if (i < EPSILON) {
 | |
|             if (Marked[nextleft] && Marked[nextright]) {
 | |
|                 /* Arrgh, we're trapped! (no more points)
 | |
|                  * Out, we've got to get out of here before this decadence saps
 | |
|                  * our will completely! */
 | |
|                 break;
 | |
|             }
 | |
|             continue;
 | |
|         }
 | |
|         else {
 | |
|             j = (int) i;
 | |
|             if (!j)
 | |
|                 j++;
 | |
|         }
 | |
|         while (j > 0) {
 | |
|             int cxl, cxr;
 | |
| 
 | |
|             ptsOut->y = (y) + yTrans;
 | |
| 
 | |
|             cxl = ICEIL(xl);
 | |
|             cxr = ICEIL(xr);
 | |
|             /* reverse the edges if necessary */
 | |
|             if (xl < xr) {
 | |
|                 *(width++) = cxr - cxl;
 | |
|                 (ptsOut++)->x = cxl + xTrans;
 | |
|             }
 | |
|             else {
 | |
|                 *(width++) = cxl - cxr;
 | |
|                 (ptsOut++)->x = cxr + xTrans;
 | |
|             }
 | |
|             y++;
 | |
| 
 | |
|             /* increment down the edges */
 | |
|             xl += ml;
 | |
|             xr += mr;
 | |
|             j--;
 | |
|         }
 | |
|     } while (y <= ymax);
 | |
| 
 | |
|     /* Finally, fill the spans we've collected */
 | |
|     (*pgc->ops->FillSpans) (dst, pgc,
 | |
|                             ptsOut - FirstPoint, FirstPoint, FirstWidth, 1);
 | |
|     free(Marked);
 | |
|     free(FirstWidth);
 | |
|     free(FirstPoint);
 | |
| }
 | |
| static double
 | |
| angleBetween(SppPointRec center, SppPointRec point1, SppPointRec 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(miArcFacePtr b, int x, int y, double fx, double 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(DrawablePtr pDraw, GCPtr pGC, miArcFacePtr pLeft,
 | |
|           miArcFacePtr pRight, int xOrgLeft, int yOrgLeft,
 | |
|           double xFtransLeft, double yFtransLeft,
 | |
|           int xOrgRight, int yOrgRight,
 | |
|           double xFtransRight, double yFtransRight)
 | |
| {
 | |
|     SppPointRec center, corner, otherCorner;
 | |
|     SppPointRec poly[5], e;
 | |
|     SppPointPtr pArcPts;
 | |
|     int cpt;
 | |
|     SppArcRec arc;
 | |
|     miArcFaceRec Right, Left;
 | |
|     int polyLen = 0;
 | |
|     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 = calloc(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);
 | |
|         }
 | |
|         free(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(DrawablePtr pDraw,
 | |
|          GCPtr pGC,
 | |
|          miArcFacePtr pFace,
 | |
|          int end, int xOrg, int yOrg, double xFtrans, double 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(DrawablePtr pDraw,
 | |
|            GCPtr pGC,
 | |
|            SppPointRec pCenter,
 | |
|            SppPointRec pEnd,
 | |
|            SppPointRec pCorner,
 | |
|            SppPointRec pOtherCorner,
 | |
|            int fLineEnd, int xOrg, int yOrg, double xFtrans, double yFtrans)
 | |
| {
 | |
|     int cpt;
 | |
|     double width;
 | |
|     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);
 | |
|     }
 | |
|     free(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(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(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(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(double dy, double 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 calloc()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 realloc() to handle the null pointer correctly.
 | |
|  */
 | |
| static int
 | |
| miGetArcPts(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;
 | |
| 
 | |
|     /* 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 = reallocarray(*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);
 | |
|     poly[cpt + 1].x = (xc + x1);
 | |
|     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 (fabs(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(miArcCapPtr * capsp, int *ncapsp, int *sizep, int end, int arcIndex)
 | |
| {
 | |
|     int newsize;
 | |
|     miArcCapPtr cap;
 | |
| 
 | |
|     if (*ncapsp == *sizep) {
 | |
|         newsize = *sizep + ADD_REALLOC_STEP;
 | |
|         cap = reallocarray(*capsp, newsize, sizeof(**capsp));
 | |
|         if (!cap)
 | |
|             return;
 | |
|         *sizep = newsize;
 | |
|         *capsp = cap;
 | |
|     }
 | |
|     cap = &(*capsp)[*ncapsp];
 | |
|     cap->end = end;
 | |
|     cap->arcIndex = arcIndex;
 | |
|     ++*ncapsp;
 | |
| }
 | |
| 
 | |
| static void
 | |
| addJoin(miArcJoinPtr * joinsp,
 | |
|         int *njoinsp,
 | |
|         int *sizep,
 | |
|         int end0, int index0, int phase0, int end1, int index1, int phase1)
 | |
| {
 | |
|     int newsize;
 | |
|     miArcJoinPtr join;
 | |
| 
 | |
|     if (*njoinsp == *sizep) {
 | |
|         newsize = *sizep + ADD_REALLOC_STEP;
 | |
|         join = reallocarray(*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(miArcDataPtr * arcsp, int *narcsp, int *sizep, xArc * xarc)
 | |
| {
 | |
|     int newsize;
 | |
|     miArcDataPtr arc;
 | |
| 
 | |
|     if (*narcsp == *sizep) {
 | |
|         newsize = *sizep + ADD_REALLOC_STEP;
 | |
|         arc = reallocarray(*arcsp, newsize, sizeof(**arcsp));
 | |
|         if (!arc)
 | |
|             return NULL;
 | |
|         *sizep = newsize;
 | |
|         *arcsp = arc;
 | |
|     }
 | |
|     arc = &(*arcsp)[*narcsp];
 | |
|     arc->arc = *xarc;
 | |
|     ++*narcsp;
 | |
|     return arc;
 | |
| }
 | |
| 
 | |
| static void
 | |
| miFreeArcs(miPolyArcPtr arcs, GCPtr pGC)
 | |
| {
 | |
|     int iphase;
 | |
| 
 | |
|     for (iphase = ((pGC->lineStyle == LineDoubleDash) ? 1 : 0);
 | |
|          iphase >= 0; iphase--) {
 | |
|         if (arcs[iphase].narcs > 0)
 | |
|             free(arcs[iphase].arcs);
 | |
|         if (arcs[iphase].njoins > 0)
 | |
|             free(arcs[iphase].joins);
 | |
|         if (arcs[iphase].ncaps > 0)
 | |
|             free(arcs[iphase].caps);
 | |
|     }
 | |
|     free(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 int computeAngleFromPath(int startAngle, int endAngle, dashMap * map,
 | |
|                                 int *lenp, int backwards);
 | |
| 
 | |
| static void
 | |
| computeDashMap(xArc * arcp, dashMap * map)
 | |
| {
 | |
|     int di;
 | |
|     double a, x, y, prevx = 0.0, prevy = 0.0, 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(xArc * parcs, int narcs, GCPtr pGC)
 | |
| {
 | |
|     int isDashed, isDoubleDash;
 | |
|     int dashOffset;
 | |
|     miPolyArcPtr arcs;
 | |
|     int start, i, j, k = 0, nexti, nextk = 0;
 | |
|     int joinSize[2];
 | |
|     int capSize[2];
 | |
|     int arcSize[2];
 | |
|     int angle2;
 | |
|     double a0, a1;
 | |
|     struct arcData *data;
 | |
|     miArcDataPtr arc;
 | |
|     xArc xarc;
 | |
|     int iphase, prevphase = 0, joinphase;
 | |
|     int arcsJoin;
 | |
|     int selfJoin;
 | |
| 
 | |
|     int iDash = 0, dashRemaining = 0;
 | |
|     int iDashStart = 0, dashRemainingStart = 0, iphaseStart;
 | |
|     int startAngle, spanAngle, endAngle, backwards = 0;
 | |
|     int prevDashAngle, dashAngle;
 | |
|     dashMap map;
 | |
| 
 | |
|     isDashed = !(pGC->lineStyle == LineSolid);
 | |
|     isDoubleDash = (pGC->lineStyle == LineDoubleDash);
 | |
|     dashOffset = pGC->dashOffset;
 | |
| 
 | |
|     data = calloc(narcs, sizeof(struct arcData));
 | |
|     if (!data)
 | |
|         return NULL;
 | |
|     arcs = calloc(isDoubleDash ? 2 : 1, sizeof(*arcs));
 | |
|     if (!arcs) {
 | |
|         free(data);
 | |
|         return 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;
 | |
|         }
 | |
|     free(data);
 | |
|     return arcs;
 | |
|  arcfail:
 | |
|     miFreeArcs(arcs, pGC);
 | |
|     free(data);
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| static double
 | |
| angleToLength(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(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 corresponds 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(int startAngle, int 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(DrawablePtr pDraw,
 | |
|             GCPtr pGC,
 | |
|             xArc * tarc, int lw, miArcFacePtr left, miArcFacePtr right)
 | |
| {
 | |
|     double x0 = 0.0, y0 = 0.0, x1 = 0.0, y1 = 0.0, 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(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(double x, double 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(double x, double 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(double x, double 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(double x, double 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(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(double x1, double y1, double x2, double 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(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(struct arc_def *def,
 | |
|              struct arc_bound *bound,
 | |
|              struct accelerators *acc, miArcFacePtr right, miArcFacePtr 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(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(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(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(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(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;
 | |
| 
 | |
| static struct finalSpan *
 | |
| realAllocSpan(void)
 | |
| {
 | |
|     struct finalSpan *span;
 | |
|     int i;
 | |
| 
 | |
|     struct finalSpanChunk *newChunk = calloc(1, 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(void)
 | |
| {
 | |
|     struct finalSpanChunk *chunk, *next;
 | |
| 
 | |
|     for (chunk = chunks; chunk; chunk = next) {
 | |
|         next = chunk->next;
 | |
|         free(chunk);
 | |
|     }
 | |
|     chunks = 0;
 | |
|     freeFinalSpans = 0;
 | |
|     free(finalSpans);
 | |
|     finalSpans = 0;
 | |
| }
 | |
| 
 | |
| static void
 | |
| fillSpans(DrawablePtr pDrawable, GCPtr pGC)
 | |
| {
 | |
|     struct finalSpan *span;
 | |
|     DDXPointPtr xSpan;
 | |
|     int *xWidth;
 | |
|     int i;
 | |
|     struct finalSpan **f;
 | |
|     int spany;
 | |
|     DDXPointPtr xSpans;
 | |
|     int *xWidths;
 | |
| 
 | |
|     if (nspans == 0)
 | |
|         return;
 | |
|     xSpan = xSpans = calloc(nspans, sizeof(DDXPointRec));
 | |
|     xWidth = xWidths = calloc(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();
 | |
|     free(xSpans);
 | |
|     free(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(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 = calloc(newSize, sizeof(struct finalSpan *));
 | |
|         if (!newSpans)
 | |
|             return NULL;
 | |
|         newMiny = finalMiny;
 | |
|         newMaxy = finalMaxy;
 | |
|         if (y < finalMiny)
 | |
|             newMiny = finalMiny - change;
 | |
|         else
 | |
|             newMaxy = finalMaxy + change;
 | |
|         if (finalSpans) {
 | |
|             memcpy(((char *) newSpans) +
 | |
|                     (finalMiny - newMiny) * sizeof(struct finalSpan *),
 | |
|                    finalSpans,
 | |
|                    finalSize * sizeof(struct finalSpan *));
 | |
|             free(finalSpans);
 | |
|         }
 | |
|         if ((i = finalMiny - newMiny) > 0)
 | |
|             memset((char *) newSpans, 0, i * sizeof(struct finalSpan *));
 | |
|         if ((i = newMaxy - finalMaxy) > 0)
 | |
|             memset((char *) (newSpans + newSize - i), 0,
 | |
|                    i * sizeof(struct finalSpan *));
 | |
|         finalSpans = newSpans;
 | |
|         finalMaxy = newMaxy;
 | |
|         finalMiny = newMiny;
 | |
|         finalSize = newSize;
 | |
|     }
 | |
|     return &finalSpans[y - finalMiny];
 | |
| }
 | |
| 
 | |
| static void
 | |
| newFinalSpan(int y, int xmin, int xmax)
 | |
| {
 | |
|     struct finalSpan *x;
 | |
|     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(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 miArcSpanData *
 | |
| drawArc(xArc * tarc, int l, int a0, int a1, miArcFacePtr right,
 | |
|         miArcFacePtr left, miArcSpanData *spdata)
 | |
| {                               /* save end line points */
 | |
|     struct arc_def def;
 | |
|     struct accelerators acc;
 | |
|     int startq, endq, curq;
 | |
|     int rightq, leftq = 0, righta = 0, lefta = 0;
 | |
|     miArcFacePtr passRight, passLeft;
 | |
|     int q0 = 0, q1 = 0, 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;
 | |
| 
 | |
|     if (!spdata)
 | |
|         spdata = miComputeWideEllipse(l, tarc);
 | |
|     if (!spdata)
 | |
|         return NULL;
 | |
| 
 | |
|     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;
 | |
|         }
 | |
|     }
 | |
|     return spdata;
 | |
| }
 | |
| 
 | |
| static void
 | |
| drawQuadrant(struct arc_def *def,
 | |
|              struct accelerators *acc,
 | |
|              int a0,
 | |
|              int a1,
 | |
|              int mask,
 | |
|              miArcFacePtr right, miArcFacePtr 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--;
 | |
|     }
 | |
| }
 | |
| 
 | |
| void
 | |
| miPolyArc(DrawablePtr pDraw, GCPtr pGC, int narcs, xArc * parcs)
 | |
| {
 | |
|     if (pGC->lineWidth == 0)
 | |
|         miZeroPolyArc(pDraw, pGC, narcs, parcs);
 | |
|     else
 | |
|         miWideArc(pDraw, pGC, narcs, parcs);
 | |
| }
 |