1 /***********************************************************
2 
3 Copyright 1987, 1998  The Open Group
4 
5 Permission to use, copy, modify, distribute, and sell this software and its
6 documentation for any purpose is hereby granted without fee, provided that
7 the above copyright notice appear in all copies and that both that
8 copyright notice and this permission notice appear in supporting
9 documentation.
10 
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13 
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 
21 Except as contained in this notice, the name of The Open Group shall not be
22 used in advertising or otherwise to promote the sale, use or other dealings
23 in this Software without prior written authorization from The Open Group.
24 
25 Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
26 
27                         All Rights Reserved
28 
29 Permission to use, copy, modify, and distribute this software and its
30 documentation for any purpose and without fee is hereby granted,
31 provided that the above copyright notice appear in all copies and that
32 both that copyright notice and this permission notice appear in
33 supporting documentation, and that the name of Digital not be
34 used in advertising or publicity pertaining to distribution of the
35 software without specific, written prior permission.
36 
37 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
38 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
39 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
40 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
41 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
42 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
43 SOFTWARE.
44 
45 ******************************************************************/
46 /* Author: Keith Packard and Bob Scheifler */
47 /* Warning: this code is toxic, do not dally very long here. */
48 
49 #ifdef HAVE_DIX_CONFIG_H
50 #include <dix-config.h>
51 #endif
52 
53 #include <math.h>
54 #include <X11/X.h>
55 #include <X11/Xprotostr.h>
56 #include "misc.h"
57 #include "gcstruct.h"
58 #include "scrnintstr.h"
59 #include "pixmapstr.h"
60 #include "windowstr.h"
61 #include "mifpoly.h"
62 #include "mi.h"
63 #include "mifillarc.h"
64 #include <X11/Xfuncproto.h>
65 
66 #define EPSILON	0.000001
67 #define ISEQUAL(a,b) (fabs((a) - (b)) <= EPSILON)
68 #define UNEQUAL(a,b) (fabs((a) - (b)) > EPSILON)
69 #define PTISEQUAL(a,b) (ISEQUAL(a.x,b.x) && ISEQUAL(a.y,b.y))
70 #define SQSECANT 108.856472512142   /* 1/sin^2(11/2) - for 11o miter cutoff */
71 
72 /* Point with sub-pixel positioning. */
73 typedef struct _SppPoint {
74     double x, y;
75 } SppPointRec, *SppPointPtr;
76 
77 typedef struct _SppArc {
78     double x, y, width, height;
79     double angle1, angle2;
80 } SppArcRec, *SppArcPtr;
81 
82 static double miDsin(double a);
83 static double miDcos(double a);
84 static double miDasin(double v);
85 static double miDatan2(double dy, double dx);
86 
87 #ifndef HAVE_CBRT
88 static double
cbrt(double x)89 cbrt(double x)
90 {
91     if (x > 0.0)
92         return pow(x, 1.0 / 3.0);
93     else
94         return -pow(-x, 1.0 / 3.0);
95 }
96 #endif
97 
98 /*
99  * some interesting semantic interpretation of the protocol:
100  *
101  * Self intersecting arcs (i.e. those spanning 360 degrees)
102  *  never join with other arcs, and are drawn without caps
103  *  (unless on/off dashed, in which case each dash segment
104  *  is capped, except when the last segment meets the
105  *  first segment, when no caps are drawn)
106  *
107  * double dash arcs are drawn in two parts, first the
108  *  odd dashes (drawn in background) then the even dashes
109  *  (drawn in foreground).  This means that overlapping
110  *  sections of foreground/background are drawn twice,
111  *  first in background then in foreground.  The double-draw
112  *  occurs even when the function uses the destination values
113  *  (e.g. xor mode).  This is the same way the wide-line
114  *  code works and should be "fixed".
115  *
116  */
117 
118 struct bound {
119     double min, max;
120 };
121 
122 struct ibound {
123     int min, max;
124 };
125 
126 #define boundedLe(value, bounds)\
127 	((bounds).min <= (value) && (value) <= (bounds).max)
128 
129 struct line {
130     double m, b;
131     int valid;
132 };
133 
134 #define intersectLine(y,line) (line.m * (y) + line.b)
135 
136 /*
137  * these are all y value bounds
138  */
139 
140 struct arc_bound {
141     struct bound ellipse;
142     struct bound inner;
143     struct bound outer;
144     struct bound right;
145     struct bound left;
146     struct ibound inneri;
147     struct ibound outeri;
148 };
149 
150 struct accelerators {
151     double tail_y;
152     double h2;
153     double w2;
154     double h4;
155     double w4;
156     double h2mw2;
157     double h2l;
158     double w2l;
159     double fromIntX;
160     double fromIntY;
161     struct line left, right;
162     int yorgu;
163     int yorgl;
164     int xorg;
165 };
166 
167 struct arc_def {
168     double w, h, l;
169     double a0, a1;
170 };
171 
172 #define todeg(xAngle)	(((double) (xAngle)) / 64.0)
173 
174 #define RIGHT_END	0
175 #define LEFT_END	1
176 
177 typedef struct _miArcJoin {
178     int arcIndex0, arcIndex1;
179     int phase0, phase1;
180     int end0, end1;
181 } miArcJoinRec, *miArcJoinPtr;
182 
183 typedef struct _miArcCap {
184     int arcIndex;
185     int end;
186 } miArcCapRec, *miArcCapPtr;
187 
188 typedef struct _miArcFace {
189     SppPointRec clock;
190     SppPointRec center;
191     SppPointRec counterClock;
192 } miArcFaceRec, *miArcFacePtr;
193 
194 typedef struct _miArcData {
195     xArc arc;
196     int render;                 /* non-zero means render after drawing */
197     int join;                   /* related join */
198     int cap;                    /* related cap */
199     int selfJoin;               /* final dash meets first dash */
200     miArcFaceRec bounds[2];
201     double x0, y0, x1, y1;
202 } miArcDataRec, *miArcDataPtr;
203 
204 /*
205  * This is an entire sequence of arcs, computed and categorized according
206  * to operation.  miDashArcs generates either one or two of these.
207  */
208 
209 typedef struct _miPolyArc {
210     int narcs;
211     miArcDataPtr arcs;
212     int ncaps;
213     miArcCapPtr caps;
214     int njoins;
215     miArcJoinPtr joins;
216 } miPolyArcRec, *miPolyArcPtr;
217 
218 typedef struct {
219     short lx, lw, rx, rw;
220 } miArcSpan;
221 
222 typedef struct {
223     miArcSpan *spans;
224     int count1, count2, k;
225     char top, bot, hole;
226 } miArcSpanData;
227 
228 static void fillSpans(DrawablePtr pDrawable, GCPtr pGC);
229 static void newFinalSpan(int y, int xmin, int xmax);
230 static miArcSpanData *drawArc(xArc * tarc, int l, int a0, int a1,
231                               miArcFacePtr right, miArcFacePtr left,
232                               miArcSpanData *spdata);
233 static void drawZeroArc(DrawablePtr pDraw, GCPtr pGC, xArc * tarc, int lw,
234                         miArcFacePtr left, miArcFacePtr right);
235 static void miArcJoin(DrawablePtr pDraw, GCPtr pGC, miArcFacePtr pLeft,
236                       miArcFacePtr pRight, int xOrgLeft, int yOrgLeft,
237                       double xFtransLeft, double yFtransLeft,
238                       int xOrgRight, int yOrgRight,
239                       double xFtransRight, double yFtransRight);
240 static void miArcCap(DrawablePtr pDraw, GCPtr pGC, miArcFacePtr pFace,
241                      int end, int xOrg, int yOrg, double xFtrans,
242                      double yFtrans);
243 static void miRoundCap(DrawablePtr pDraw, GCPtr pGC, SppPointRec pCenter,
244                        SppPointRec pEnd, SppPointRec pCorner,
245                        SppPointRec pOtherCorner, int fLineEnd,
246                        int xOrg, int yOrg, double xFtrans, double yFtrans);
247 static void miFreeArcs(miPolyArcPtr arcs, GCPtr pGC);
248 static miPolyArcPtr miComputeArcs(xArc * parcs, int narcs, GCPtr pGC);
249 static int miGetArcPts(SppArcPtr parc, int cpt, SppPointPtr * ppPts);
250 
251 #define CUBED_ROOT_2	1.2599210498948732038115849718451499938964
252 #define CUBED_ROOT_4	1.5874010519681993173435330390930175781250
253 
254 /*
255  * draw one segment of the arc using the arc spans generation routines
256  */
257 
258 static miArcSpanData *
miArcSegment(DrawablePtr pDraw,GCPtr pGC,xArc tarc,miArcFacePtr right,miArcFacePtr left,miArcSpanData * spdata)259 miArcSegment(DrawablePtr pDraw, GCPtr pGC, xArc tarc, miArcFacePtr right,
260              miArcFacePtr left, miArcSpanData *spdata)
261 {
262     int l = pGC->lineWidth;
263     int a0, a1, startAngle, endAngle;
264     miArcFacePtr temp;
265 
266     if (!l)
267         l = 1;
268 
269     if (tarc.width == 0 || tarc.height == 0) {
270         drawZeroArc(pDraw, pGC, &tarc, l, left, right);
271         return spdata;
272     }
273 
274     if (pGC->miTranslate) {
275         tarc.x += pDraw->x;
276         tarc.y += pDraw->y;
277     }
278 
279     a0 = tarc.angle1;
280     a1 = tarc.angle2;
281     if (a1 > FULLCIRCLE)
282         a1 = FULLCIRCLE;
283     else if (a1 < -FULLCIRCLE)
284         a1 = -FULLCIRCLE;
285     if (a1 < 0) {
286         startAngle = a0 + a1;
287         endAngle = a0;
288         temp = right;
289         right = left;
290         left = temp;
291     }
292     else {
293         startAngle = a0;
294         endAngle = a0 + a1;
295     }
296     /*
297      * bounds check the two angles
298      */
299     if (startAngle < 0)
300         startAngle = FULLCIRCLE - (-startAngle) % FULLCIRCLE;
301     if (startAngle >= FULLCIRCLE)
302         startAngle = startAngle % FULLCIRCLE;
303     if (endAngle < 0)
304         endAngle = FULLCIRCLE - (-endAngle) % FULLCIRCLE;
305     if (endAngle > FULLCIRCLE)
306         endAngle = (endAngle - 1) % FULLCIRCLE + 1;
307     if ((startAngle == endAngle) && a1) {
308         startAngle = 0;
309         endAngle = FULLCIRCLE;
310     }
311 
312     return drawArc(&tarc, l, startAngle, endAngle, right, left, spdata);
313 }
314 
315 /*
316 
317 Three equations combine to describe the boundaries of the arc
318 
319 x^2/w^2 + y^2/h^2 = 1			ellipse itself
320 (X-x)^2 + (Y-y)^2 = r^2			circle at (x, y) on the ellipse
321 (Y-y) = (X-x)*w^2*y/(h^2*x)		normal at (x, y) on the ellipse
322 
323 These lead to a quartic relating Y and y
324 
325 y^4 - (2Y)y^3 + (Y^2 + (h^4 - w^2*r^2)/(w^2 - h^2))y^2
326     - (2Y*h^4/(w^2 - h^2))y + (Y^2*h^4)/(w^2 - h^2) = 0
327 
328 The reducible cubic obtained from this quartic is
329 
330 z^3 - (3N)z^2 - 2V = 0
331 
332 where
333 
334 N = (Y^2 + (h^4 - w^2*r^2/(w^2 - h^2)))/6
335 V = w^2*r^2*Y^2*h^4/(4 *(w^2 - h^2)^2)
336 
337 Let
338 
339 t = z - N
340 p = -N^2
341 q = -N^3 - V
342 
343 Then we get
344 
345 t^3 + 3pt + 2q = 0
346 
347 The discriminant of this cubic is
348 
349 D = q^2 + p^3
350 
351 When D > 0, a real root is obtained as
352 
353 z = N + cbrt(-q+sqrt(D)) + cbrt(-q-sqrt(D))
354 
355 When D < 0, a real root is obtained as
356 
357 z = N - 2m*cos(acos(-q/m^3)/3)
358 
359 where
360 
361 m = sqrt(|p|) * sign(q)
362 
363 Given a real root Z of the cubic, the roots of the quartic are the roots
364 of the two quadratics
365 
366 y^2 + ((b+A)/2)y + (Z + (bZ - d)/A) = 0
367 
368 where
369 
370 A = +/- sqrt(8Z + b^2 - 4c)
371 b, c, d are the cubic, quadratic, and linear coefficients of the quartic
372 
373 Some experimentation is then required to determine which solutions
374 correspond to the inner and outer boundaries.
375 
376 */
377 
378 static void drawQuadrant(struct arc_def *def, struct accelerators *acc,
379                          int a0, int a1, int mask, miArcFacePtr right,
380                          miArcFacePtr left, miArcSpanData * spdata);
381 
382 static void
miComputeCircleSpans(int lw,xArc * parc,miArcSpanData * spdata)383 miComputeCircleSpans(int lw, xArc * parc, miArcSpanData * spdata)
384 {
385     miArcSpan *span;
386     int doinner;
387     int x, y, e;
388     int xk, yk, xm, ym, dx, dy;
389     int slw, inslw;
390     int inx = 0, iny, ine = 0;
391     int inxk = 0, inyk = 0, inxm = 0, inym = 0;
392 
393     doinner = -lw;
394     slw = parc->width - doinner;
395     y = parc->height >> 1;
396     dy = parc->height & 1;
397     dx = 1 - dy;
398     MIWIDEARCSETUP(x, y, dy, slw, e, xk, xm, yk, ym);
399     inslw = parc->width + doinner;
400     if (inslw > 0) {
401         spdata->hole = spdata->top;
402         MIWIDEARCSETUP(inx, iny, dy, inslw, ine, inxk, inxm, inyk, inym);
403     }
404     else {
405         spdata->hole = FALSE;
406         doinner = -y;
407     }
408     spdata->count1 = -doinner - spdata->top;
409     spdata->count2 = y + doinner;
410     span = spdata->spans;
411     while (y) {
412         MIFILLARCSTEP(slw);
413         span->lx = dy - x;
414         if (++doinner <= 0) {
415             span->lw = slw;
416             span->rx = 0;
417             span->rw = span->lx + slw;
418         }
419         else {
420             MIFILLINARCSTEP(inslw);
421             span->lw = x - inx;
422             span->rx = dy - inx + inslw;
423             span->rw = inx - x + slw - inslw;
424         }
425         span++;
426     }
427     if (spdata->bot) {
428         if (spdata->count2)
429             spdata->count2--;
430         else {
431             if (lw > (int) parc->height)
432                 span[-1].rx = span[-1].rw = -((lw - (int) parc->height) >> 1);
433             else
434                 span[-1].rw = 0;
435             spdata->count1--;
436         }
437     }
438 }
439 
440 static void
miComputeEllipseSpans(int lw,xArc * parc,miArcSpanData * spdata)441 miComputeEllipseSpans(int lw, xArc * parc, miArcSpanData * spdata)
442 {
443     miArcSpan *span;
444     double w, h, r, xorg;
445     double Hs, Hf, WH, K, Vk, Nk, Fk, Vr, N, Nc, Z, rs;
446     double A, T, b, d, x, y, t, inx, outx = 0.0, hepp, hepm;
447     int flip, solution;
448 
449     w = (double) parc->width / 2.0;
450     h = (double) parc->height / 2.0;
451     r = lw / 2.0;
452     rs = r * r;
453     Hs = h * h;
454     WH = w * w - Hs;
455     Nk = w * r;
456     Vk = (Nk * Hs) / (WH + WH);
457     Hf = Hs * Hs;
458     Nk = (Hf - Nk * Nk) / WH;
459     Fk = Hf / WH;
460     hepp = h + EPSILON;
461     hepm = h - EPSILON;
462     K = h + ((lw - 1) >> 1);
463     span = spdata->spans;
464     if (parc->width & 1)
465         xorg = .5;
466     else
467         xorg = 0.0;
468     if (spdata->top) {
469         span->lx = 0;
470         span->lw = 1;
471         span++;
472     }
473     spdata->count1 = 0;
474     spdata->count2 = 0;
475     spdata->hole = (spdata->top &&
476                     (int) parc->height * lw <= (int) (parc->width * parc->width)
477                     && lw < (int) parc->height);
478     for (; K > 0.0; K -= 1.0) {
479         N = (K * K + Nk) / 6.0;
480         Nc = N * N * N;
481         Vr = Vk * K;
482         t = Nc + Vr * Vr;
483         d = Nc + t;
484         if (d < 0.0) {
485             d = Nc;
486             b = N;
487             if ((b < 0.0) == (t < 0.0)) {
488                 b = -b;
489                 d = -d;
490             }
491             Z = N - 2.0 * b * cos(acos(-t / d) / 3.0);
492             if ((Z < 0.0) == (Vr < 0.0))
493                 flip = 2;
494             else
495                 flip = 1;
496         }
497         else {
498             d = Vr * sqrt(d);
499             Z = N + cbrt(t + d) + cbrt(t - d);
500             flip = 0;
501         }
502         A = sqrt((Z + Z) - Nk);
503         T = (Fk - Z) * K / A;
504         inx = 0.0;
505         solution = FALSE;
506         b = -A + K;
507         d = b * b - 4 * (Z + T);
508         if (d >= 0) {
509             d = sqrt(d);
510             y = (b + d) / 2;
511             if ((y >= 0.0) && (y < hepp)) {
512                 solution = TRUE;
513                 if (y > hepm)
514                     y = h;
515                 t = y / h;
516                 x = w * sqrt(1 - (t * t));
517                 t = K - y;
518                 if (rs - (t * t) >= 0)
519                     t = sqrt(rs - (t * t));
520                 else
521                     t = 0;
522                 if (flip == 2)
523                     inx = x - t;
524                 else
525                     outx = x + t;
526             }
527         }
528         b = A + K;
529         d = b * b - 4 * (Z - T);
530         /* Because of the large magnitudes involved, we lose enough precision
531          * that sometimes we end up with a negative value near the axis, when
532          * it should be positive.  This is a workaround.
533          */
534         if (d < 0 && !solution)
535             d = 0.0;
536         if (d >= 0) {
537             d = sqrt(d);
538             y = (b + d) / 2;
539             if (y < hepp) {
540                 if (y > hepm)
541                     y = h;
542                 t = y / h;
543                 x = w * sqrt(1 - (t * t));
544                 t = K - y;
545                 if (rs - (t * t) >= 0)
546                     inx = x - sqrt(rs - (t * t));
547                 else
548                     inx = x;
549             }
550             y = (b - d) / 2;
551             if (y >= 0.0) {
552                 if (y > hepm)
553                     y = h;
554                 t = y / h;
555                 x = w * sqrt(1 - (t * t));
556                 t = K - y;
557                 if (rs - (t * t) >= 0)
558                     t = sqrt(rs - (t * t));
559                 else
560                     t = 0;
561                 if (flip == 1)
562                     inx = x - t;
563                 else
564                     outx = x + t;
565             }
566         }
567         span->lx = ICEIL(xorg - outx);
568         if (inx <= 0.0) {
569             spdata->count1++;
570             span->lw = ICEIL(xorg + outx) - span->lx;
571             span->rx = ICEIL(xorg + inx);
572             span->rw = -ICEIL(xorg - inx);
573         }
574         else {
575             spdata->count2++;
576             span->lw = ICEIL(xorg - inx) - span->lx;
577             span->rx = ICEIL(xorg + inx);
578             span->rw = ICEIL(xorg + outx) - span->rx;
579         }
580         span++;
581     }
582     if (spdata->bot) {
583         outx = w + r;
584         if (r >= h && r <= w)
585             inx = 0.0;
586         else if (Nk < 0.0 && -Nk < Hs) {
587             inx = w * sqrt(1 + Nk / Hs) - sqrt(rs + Nk);
588             if (inx > w - r)
589                 inx = w - r;
590         }
591         else
592             inx = w - r;
593         span->lx = ICEIL(xorg - outx);
594         if (inx <= 0.0) {
595             span->lw = ICEIL(xorg + outx) - span->lx;
596             span->rx = ICEIL(xorg + inx);
597             span->rw = -ICEIL(xorg - inx);
598         }
599         else {
600             span->lw = ICEIL(xorg - inx) - span->lx;
601             span->rx = ICEIL(xorg + inx);
602             span->rw = ICEIL(xorg + outx) - span->rx;
603         }
604     }
605     if (spdata->hole) {
606         span = &spdata->spans[spdata->count1];
607         span->lw = -span->lx;
608         span->rx = 1;
609         span->rw = span->lw;
610         spdata->count1--;
611         spdata->count2++;
612     }
613 }
614 
615 static double
tailX(double K,struct arc_def * def,struct arc_bound * bounds,struct accelerators * acc)616 tailX(double K,
617       struct arc_def *def, struct arc_bound *bounds, struct accelerators *acc)
618 {
619     double w, h, r;
620     double Hs, Hf, WH, Vk, Nk, Fk, Vr, N, Nc, Z, rs;
621     double A, T, b, d, x, y, t, hepp, hepm;
622     int flip, solution;
623     double xs[2];
624     double *xp;
625 
626     w = def->w;
627     h = def->h;
628     r = def->l;
629     rs = r * r;
630     Hs = acc->h2;
631     WH = -acc->h2mw2;
632     Nk = def->w * r;
633     Vk = (Nk * Hs) / (WH + WH);
634     Hf = acc->h4;
635     Nk = (Hf - Nk * Nk) / WH;
636     if (K == 0.0) {
637         if (Nk < 0.0 && -Nk < Hs) {
638             xs[0] = w * sqrt(1 + Nk / Hs) - sqrt(rs + Nk);
639             xs[1] = w - r;
640             if (acc->left.valid && boundedLe(K, bounds->left) &&
641                 !boundedLe(K, bounds->outer) && xs[0] >= 0.0 && xs[1] >= 0.0)
642                 return xs[1];
643             if (acc->right.valid && boundedLe(K, bounds->right) &&
644                 !boundedLe(K, bounds->inner) && xs[0] <= 0.0 && xs[1] <= 0.0)
645                 return xs[1];
646             return xs[0];
647         }
648         return w - r;
649     }
650     Fk = Hf / WH;
651     hepp = h + EPSILON;
652     hepm = h - EPSILON;
653     N = (K * K + Nk) / 6.0;
654     Nc = N * N * N;
655     Vr = Vk * K;
656     xp = xs;
657     xs[0] = 0.0;
658     t = Nc + Vr * Vr;
659     d = Nc + t;
660     if (d < 0.0) {
661         d = Nc;
662         b = N;
663         if ((b < 0.0) == (t < 0.0)) {
664             b = -b;
665             d = -d;
666         }
667         Z = N - 2.0 * b * cos(acos(-t / d) / 3.0);
668         if ((Z < 0.0) == (Vr < 0.0))
669             flip = 2;
670         else
671             flip = 1;
672     }
673     else {
674         d = Vr * sqrt(d);
675         Z = N + cbrt(t + d) + cbrt(t - d);
676         flip = 0;
677     }
678     A = sqrt((Z + Z) - Nk);
679     T = (Fk - Z) * K / A;
680     solution = FALSE;
681     b = -A + K;
682     d = b * b - 4 * (Z + T);
683     if (d >= 0 && flip == 2) {
684         d = sqrt(d);
685         y = (b + d) / 2;
686         if ((y >= 0.0) && (y < hepp)) {
687             solution = TRUE;
688             if (y > hepm)
689                 y = h;
690             t = y / h;
691             x = w * sqrt(1 - (t * t));
692             t = K - y;
693             if (rs - (t * t) >= 0)
694                 t = sqrt(rs - (t * t));
695             else
696                 t = 0;
697             *xp++ = x - t;
698         }
699     }
700     b = A + K;
701     d = b * b - 4 * (Z - T);
702     /* Because of the large magnitudes involved, we lose enough precision
703      * that sometimes we end up with a negative value near the axis, when
704      * it should be positive.  This is a workaround.
705      */
706     if (d < 0 && !solution)
707         d = 0.0;
708     if (d >= 0) {
709         d = sqrt(d);
710         y = (b + d) / 2;
711         if (y < hepp) {
712             if (y > hepm)
713                 y = h;
714             t = y / h;
715             x = w * sqrt(1 - (t * t));
716             t = K - y;
717             if (rs - (t * t) >= 0)
718                 *xp++ = x - sqrt(rs - (t * t));
719             else
720                 *xp++ = x;
721         }
722         y = (b - d) / 2;
723         if (y >= 0.0 && flip == 1) {
724             if (y > hepm)
725                 y = h;
726             t = y / h;
727             x = w * sqrt(1 - (t * t));
728             t = K - y;
729             if (rs - (t * t) >= 0)
730                 t = sqrt(rs - (t * t));
731             else
732                 t = 0;
733             *xp++ = x - t;
734         }
735     }
736     if (xp > &xs[1]) {
737         if (acc->left.valid && boundedLe(K, bounds->left) &&
738             !boundedLe(K, bounds->outer) && xs[0] >= 0.0 && xs[1] >= 0.0)
739             return xs[1];
740         if (acc->right.valid && boundedLe(K, bounds->right) &&
741             !boundedLe(K, bounds->inner) && xs[0] <= 0.0 && xs[1] <= 0.0)
742             return xs[1];
743     }
744     return xs[0];
745 }
746 
747 static miArcSpanData *
miComputeWideEllipse(int lw,xArc * parc)748 miComputeWideEllipse(int lw, xArc * parc)
749 {
750     miArcSpanData *spdata = NULL;
751     int k;
752 
753     if (!lw)
754         lw = 1;
755     k = (parc->height >> 1) + ((lw - 1) >> 1);
756     spdata = malloc(sizeof(miArcSpanData) + sizeof(miArcSpan) * (k + 2));
757     if (!spdata)
758         return NULL;
759     spdata->spans = (miArcSpan *) (spdata + 1);
760     spdata->k = k;
761     spdata->top = !(lw & 1) && !(parc->width & 1);
762     spdata->bot = !(parc->height & 1);
763     if (parc->width == parc->height)
764         miComputeCircleSpans(lw, parc, spdata);
765     else
766         miComputeEllipseSpans(lw, parc, spdata);
767     return spdata;
768 }
769 
770 static void
miFillWideEllipse(DrawablePtr pDraw,GCPtr pGC,xArc * parc)771 miFillWideEllipse(DrawablePtr pDraw, GCPtr pGC, xArc * parc)
772 {
773     DDXPointPtr points;
774     DDXPointPtr pts;
775     int *widths;
776     int *wids;
777     miArcSpanData *spdata;
778     miArcSpan *span;
779     int xorg, yorgu, yorgl;
780     int n;
781 
782     yorgu = parc->height + pGC->lineWidth;
783     n = (sizeof(int) * 2) * yorgu;
784     widths = malloc(n + (sizeof(DDXPointRec) * 2) * yorgu);
785     if (!widths)
786         return;
787     points = (DDXPointPtr) ((char *) widths + n);
788     spdata = miComputeWideEllipse((int) pGC->lineWidth, parc);
789     if (!spdata) {
790         free(widths);
791         return;
792     }
793     pts = points;
794     wids = widths;
795     span = spdata->spans;
796     xorg = parc->x + (parc->width >> 1);
797     yorgu = parc->y + (parc->height >> 1);
798     yorgl = yorgu + (parc->height & 1);
799     if (pGC->miTranslate) {
800         xorg += pDraw->x;
801         yorgu += pDraw->y;
802         yorgl += pDraw->y;
803     }
804     yorgu -= spdata->k;
805     yorgl += spdata->k;
806     if (spdata->top) {
807         pts->x = xorg;
808         pts->y = yorgu - 1;
809         pts++;
810         *wids++ = 1;
811         span++;
812     }
813     for (n = spdata->count1; --n >= 0;) {
814         pts[0].x = xorg + span->lx;
815         pts[0].y = yorgu;
816         wids[0] = span->lw;
817         pts[1].x = pts[0].x;
818         pts[1].y = yorgl;
819         wids[1] = wids[0];
820         yorgu++;
821         yorgl--;
822         pts += 2;
823         wids += 2;
824         span++;
825     }
826     if (spdata->hole) {
827         pts[0].x = xorg;
828         pts[0].y = yorgl;
829         wids[0] = 1;
830         pts++;
831         wids++;
832     }
833     for (n = spdata->count2; --n >= 0;) {
834         pts[0].x = xorg + span->lx;
835         pts[0].y = yorgu;
836         wids[0] = span->lw;
837         pts[1].x = xorg + span->rx;
838         pts[1].y = pts[0].y;
839         wids[1] = span->rw;
840         pts[2].x = pts[0].x;
841         pts[2].y = yorgl;
842         wids[2] = wids[0];
843         pts[3].x = pts[1].x;
844         pts[3].y = pts[2].y;
845         wids[3] = wids[1];
846         yorgu++;
847         yorgl--;
848         pts += 4;
849         wids += 4;
850         span++;
851     }
852     if (spdata->bot) {
853         if (span->rw <= 0) {
854             pts[0].x = xorg + span->lx;
855             pts[0].y = yorgu;
856             wids[0] = span->lw;
857             pts++;
858             wids++;
859         }
860         else {
861             pts[0].x = xorg + span->lx;
862             pts[0].y = yorgu;
863             wids[0] = span->lw;
864             pts[1].x = xorg + span->rx;
865             pts[1].y = pts[0].y;
866             wids[1] = span->rw;
867             pts += 2;
868             wids += 2;
869         }
870     }
871     free(spdata);
872     (*pGC->ops->FillSpans) (pDraw, pGC, pts - points, points, widths, FALSE);
873 
874     free(widths);
875 }
876 
877 /*
878  * miPolyArc strategy:
879  *
880  * If arc is zero width and solid, we don't have to worry about the rasterop
881  * or join styles.  For wide solid circles, we use a fast integer algorithm.
882  * For wide solid ellipses, we use special case floating point code.
883  * Otherwise, we set up pDrawTo and pGCTo according to the rasterop, then
884  * draw using pGCTo and pDrawTo.  If the raster-op was "tricky," that is,
885  * if it involves the destination, then we use PushPixels to move the bits
886  * from the scratch drawable to pDraw. (See the wide line code for a
887  * fuller explanation of this.)
888  */
889 
890 void
miWideArc(DrawablePtr pDraw,GCPtr pGC,int narcs,xArc * parcs)891 miWideArc(DrawablePtr pDraw, GCPtr pGC, int narcs, xArc * parcs)
892 {
893     int i;
894     xArc *parc;
895     int xMin, xMax, yMin, yMax;
896     int pixmapWidth = 0, pixmapHeight = 0;
897     int xOrg = 0, yOrg = 0;
898     int width = pGC->lineWidth;
899     Bool fTricky;
900     DrawablePtr pDrawTo;
901     CARD32 fg, bg;
902     GCPtr pGCTo;
903     miPolyArcPtr polyArcs;
904     int cap[2], join[2];
905     int iphase;
906     int halfWidth;
907 
908     if (width == 0 && pGC->lineStyle == LineSolid) {
909         for (i = narcs, parc = parcs; --i >= 0; parc++) {
910             miArcSpanData *spdata;
911             spdata = miArcSegment(pDraw, pGC, *parc, NULL, NULL, NULL);
912             free(spdata);
913         }
914         fillSpans(pDraw, pGC);
915         return;
916     }
917 
918     if ((pGC->lineStyle == LineSolid) && narcs) {
919         while (parcs->width && parcs->height &&
920                (parcs->angle2 >= FULLCIRCLE || parcs->angle2 <= -FULLCIRCLE)) {
921             miFillWideEllipse(pDraw, pGC, parcs);
922             if (!--narcs)
923                 return;
924             parcs++;
925         }
926     }
927 
928     /* Set up pDrawTo and pGCTo based on the rasterop */
929     switch (pGC->alu) {
930     case GXclear:          /* 0 */
931     case GXcopy:           /* src */
932     case GXcopyInverted:   /* NOT src */
933     case GXset:            /* 1 */
934         fTricky = FALSE;
935         pDrawTo = pDraw;
936         pGCTo = pGC;
937         break;
938     default:
939         fTricky = TRUE;
940 
941         /* find bounding box around arcs */
942         xMin = yMin = MAXSHORT;
943         xMax = yMax = MINSHORT;
944 
945         for (i = narcs, parc = parcs; --i >= 0; parc++) {
946             xMin = min(xMin, parc->x);
947             yMin = min(yMin, parc->y);
948             xMax = max(xMax, (parc->x + (int) parc->width));
949             yMax = max(yMax, (parc->y + (int) parc->height));
950         }
951 
952         /* expand box to deal with line widths */
953         halfWidth = (width + 1) / 2;
954         xMin -= halfWidth;
955         yMin -= halfWidth;
956         xMax += halfWidth;
957         yMax += halfWidth;
958 
959         /* compute pixmap size; limit it to size of drawable */
960         xOrg = max(xMin, 0);
961         yOrg = max(yMin, 0);
962         pixmapWidth = min(xMax, pDraw->width) - xOrg;
963         pixmapHeight = min(yMax, pDraw->height) - yOrg;
964 
965         /* if nothing left, return */
966         if ((pixmapWidth <= 0) || (pixmapHeight <= 0))
967             return;
968 
969         for (i = narcs, parc = parcs; --i >= 0; parc++) {
970             parc->x -= xOrg;
971             parc->y -= yOrg;
972         }
973         if (pGC->miTranslate) {
974             xOrg += pDraw->x;
975             yOrg += pDraw->y;
976         }
977 
978         /* set up scratch GC */
979         pGCTo = GetScratchGC(1, pDraw->pScreen);
980         if (!pGCTo)
981             return;
982         {
983             ChangeGCVal gcvals[6];
984 
985             gcvals[0].val = GXcopy;
986             gcvals[1].val = 1;
987             gcvals[2].val = 0;
988             gcvals[3].val = pGC->lineWidth;
989             gcvals[4].val = pGC->capStyle;
990             gcvals[5].val = pGC->joinStyle;
991             ChangeGC(NullClient, pGCTo, GCFunction |
992                      GCForeground | GCBackground | GCLineWidth |
993                      GCCapStyle | GCJoinStyle, gcvals);
994         }
995 
996         /* allocate a bitmap of the appropriate size, and validate it */
997         pDrawTo = (DrawablePtr) (*pDraw->pScreen->CreatePixmap)
998             (pDraw->pScreen, pixmapWidth, pixmapHeight, 1,
999              CREATE_PIXMAP_USAGE_SCRATCH);
1000         if (!pDrawTo) {
1001             FreeScratchGC(pGCTo);
1002             return;
1003         }
1004         ValidateGC(pDrawTo, pGCTo);
1005         miClearDrawable(pDrawTo, pGCTo);
1006     }
1007 
1008     fg = pGC->fgPixel;
1009     bg = pGC->bgPixel;
1010 
1011     /* the protocol sez these don't cause color changes */
1012     if ((pGC->fillStyle == FillTiled) ||
1013         (pGC->fillStyle == FillOpaqueStippled))
1014         bg = fg;
1015 
1016     polyArcs = miComputeArcs(parcs, narcs, pGC);
1017     if (!polyArcs)
1018         goto out;
1019 
1020     cap[0] = cap[1] = 0;
1021     join[0] = join[1] = 0;
1022     for (iphase = (pGC->lineStyle == LineDoubleDash); iphase >= 0; iphase--) {
1023         miArcSpanData *spdata = NULL;
1024         xArc lastArc;
1025         ChangeGCVal gcval;
1026 
1027         if (iphase == 1) {
1028             gcval.val = bg;
1029             ChangeGC(NullClient, pGC, GCForeground, &gcval);
1030             ValidateGC(pDraw, pGC);
1031         }
1032         else if (pGC->lineStyle == LineDoubleDash) {
1033             gcval.val = fg;
1034             ChangeGC(NullClient, pGC, GCForeground, &gcval);
1035             ValidateGC(pDraw, pGC);
1036         }
1037         for (i = 0; i < polyArcs[iphase].narcs; i++) {
1038             miArcDataPtr arcData;
1039 
1040             arcData = &polyArcs[iphase].arcs[i];
1041             if (spdata) {
1042                 if (lastArc.width != arcData->arc.width ||
1043                     lastArc.height != arcData->arc.height) {
1044                     free(spdata);
1045                     spdata = NULL;
1046                 }
1047             }
1048             memcpy(&lastArc, &arcData->arc, sizeof(xArc));
1049             spdata = miArcSegment(pDrawTo, pGCTo, arcData->arc,
1050                                   &arcData->bounds[RIGHT_END],
1051                                   &arcData->bounds[LEFT_END], spdata);
1052             if (polyArcs[iphase].arcs[i].render) {
1053                 fillSpans(pDrawTo, pGCTo);
1054                 /* don't cap self-joining arcs */
1055                 if (polyArcs[iphase].arcs[i].selfJoin &&
1056                     cap[iphase] < polyArcs[iphase].arcs[i].cap)
1057                     cap[iphase]++;
1058                 while (cap[iphase] < polyArcs[iphase].arcs[i].cap) {
1059                     int arcIndex, end;
1060                     miArcDataPtr arcData0;
1061 
1062                     arcIndex = polyArcs[iphase].caps[cap[iphase]].arcIndex;
1063                     end = polyArcs[iphase].caps[cap[iphase]].end;
1064                     arcData0 = &polyArcs[iphase].arcs[arcIndex];
1065                     miArcCap(pDrawTo, pGCTo,
1066                              &arcData0->bounds[end], end,
1067                              arcData0->arc.x, arcData0->arc.y,
1068                              (double) arcData0->arc.width / 2.0,
1069                              (double) arcData0->arc.height / 2.0);
1070                     ++cap[iphase];
1071                 }
1072                 while (join[iphase] < polyArcs[iphase].arcs[i].join) {
1073                     int arcIndex0, arcIndex1, end0, end1;
1074                     int phase0, phase1;
1075                     miArcDataPtr arcData0, arcData1;
1076                     miArcJoinPtr joinp;
1077 
1078                     joinp = &polyArcs[iphase].joins[join[iphase]];
1079                     arcIndex0 = joinp->arcIndex0;
1080                     end0 = joinp->end0;
1081                     arcIndex1 = joinp->arcIndex1;
1082                     end1 = joinp->end1;
1083                     phase0 = joinp->phase0;
1084                     phase1 = joinp->phase1;
1085                     arcData0 = &polyArcs[phase0].arcs[arcIndex0];
1086                     arcData1 = &polyArcs[phase1].arcs[arcIndex1];
1087                     miArcJoin(pDrawTo, pGCTo,
1088                               &arcData0->bounds[end0],
1089                               &arcData1->bounds[end1],
1090                               arcData0->arc.x, arcData0->arc.y,
1091                               (double) arcData0->arc.width / 2.0,
1092                               (double) arcData0->arc.height / 2.0,
1093                               arcData1->arc.x, arcData1->arc.y,
1094                               (double) arcData1->arc.width / 2.0,
1095                               (double) arcData1->arc.height / 2.0);
1096                     ++join[iphase];
1097                 }
1098                 if (fTricky) {
1099                     if (pGC->serialNumber != pDraw->serialNumber)
1100                         ValidateGC(pDraw, pGC);
1101                     (*pGC->ops->PushPixels) (pGC, (PixmapPtr) pDrawTo,
1102                                              pDraw, pixmapWidth,
1103                                              pixmapHeight, xOrg, yOrg);
1104                     miClearDrawable((DrawablePtr) pDrawTo, pGCTo);
1105                 }
1106             }
1107         }
1108         free(spdata);
1109         spdata = NULL;
1110     }
1111     miFreeArcs(polyArcs, pGC);
1112 
1113 out:
1114     if (fTricky) {
1115         (*pGCTo->pScreen->DestroyPixmap) ((PixmapPtr) pDrawTo);
1116         FreeScratchGC(pGCTo);
1117     }
1118 }
1119 
1120 /* Find the index of the point with the smallest y.also return the
1121  * smallest and largest y */
1122 static int
GetFPolyYBounds(SppPointPtr pts,int n,double yFtrans,int * by,int * ty)1123 GetFPolyYBounds(SppPointPtr pts, int n, double yFtrans, int *by, int *ty)
1124 {
1125     SppPointPtr ptMin;
1126     double ymin, ymax;
1127     SppPointPtr ptsStart = pts;
1128 
1129     ptMin = pts;
1130     ymin = ymax = (pts++)->y;
1131 
1132     while (--n > 0) {
1133         if (pts->y < ymin) {
1134             ptMin = pts;
1135             ymin = pts->y;
1136         }
1137         if (pts->y > ymax)
1138             ymax = pts->y;
1139 
1140         pts++;
1141     }
1142 
1143     *by = ICEIL(ymin + yFtrans);
1144     *ty = ICEIL(ymax + yFtrans - 1);
1145     return ptMin - ptsStart;
1146 }
1147 
1148 /*
1149  *	miFillSppPoly written by Todd Newman; April. 1987.
1150  *
1151  *	Fill a convex polygon.  If the given polygon
1152  *	is not convex, then the result is undefined.
1153  *	The algorithm is to order the edges from smallest
1154  *	y to largest by partitioning the array into a left
1155  *	edge list and a right edge list.  The algorithm used
1156  *	to traverse each edge is digital differencing analyzer
1157  *	line algorithm with y as the major axis. There's some funny linear
1158  *	interpolation involved because of the subpixel postioning.
1159  */
1160 static void
miFillSppPoly(DrawablePtr dst,GCPtr pgc,int count,SppPointPtr ptsIn,int xTrans,int yTrans,double xFtrans,double yFtrans)1161 miFillSppPoly(DrawablePtr dst, GCPtr pgc, int count,    /* number of points */
1162               SppPointPtr ptsIn,        /* the points */
1163               int xTrans, int yTrans,   /* Translate each point by this */
1164               double xFtrans, double yFtrans    /* translate before conversion
1165                                                    by this amount.  This provides
1166                                                    a mechanism to match rounding
1167                                                    errors with any shape that must
1168                                                    meet the polygon exactly.
1169                                                  */
1170     )
1171 {
1172     double xl = 0.0, xr = 0.0,  /* x vals of left and right edges */
1173         ml = 0.0,               /* left edge slope */
1174         mr = 0.0,               /* right edge slope */
1175         dy,                     /* delta y */
1176         i;                      /* loop counter */
1177     int y,                      /* current scanline */
1178      j, imin,                   /* index of vertex with smallest y */
1179      ymin,                      /* y-extents of polygon */
1180      ymax, *width, *FirstWidth, /* output buffer */
1181     *Marked;                    /* set if this vertex has been used */
1182     int left, right,            /* indices to first endpoints */
1183      nextleft, nextright;       /* indices to second endpoints */
1184     DDXPointPtr ptsOut, FirstPoint;     /* output buffer */
1185 
1186     if (pgc->miTranslate) {
1187         xTrans += dst->x;
1188         yTrans += dst->y;
1189     }
1190 
1191     imin = GetFPolyYBounds(ptsIn, count, yFtrans, &ymin, &ymax);
1192 
1193     y = ymax - ymin + 1;
1194     if ((count < 3) || (y <= 0))
1195         return;
1196     ptsOut = FirstPoint = xallocarray(y, sizeof(DDXPointRec));
1197     width = FirstWidth = xallocarray(y, sizeof(int));
1198     Marked = xallocarray(count, sizeof(int));
1199 
1200     if (!ptsOut || !width || !Marked) {
1201         free(Marked);
1202         free(width);
1203         free(ptsOut);
1204         return;
1205     }
1206 
1207     for (j = 0; j < count; j++)
1208         Marked[j] = 0;
1209     nextleft = nextright = imin;
1210     Marked[imin] = -1;
1211     y = ICEIL(ptsIn[nextleft].y + yFtrans);
1212 
1213     /*
1214      *  loop through all edges of the polygon
1215      */
1216     do {
1217         /* add a left edge if we need to */
1218         if ((y > (ptsIn[nextleft].y + yFtrans) ||
1219              ISEQUAL(y, ptsIn[nextleft].y + yFtrans)) &&
1220             Marked[nextleft] != 1) {
1221             Marked[nextleft]++;
1222             left = nextleft++;
1223 
1224             /* find the next edge, considering the end conditions */
1225             if (nextleft >= count)
1226                 nextleft = 0;
1227 
1228             /* now compute the starting point and slope */
1229             dy = ptsIn[nextleft].y - ptsIn[left].y;
1230             if (dy != 0.0) {
1231                 ml = (ptsIn[nextleft].x - ptsIn[left].x) / dy;
1232                 dy = y - (ptsIn[left].y + yFtrans);
1233                 xl = (ptsIn[left].x + xFtrans) + ml * max(dy, 0);
1234             }
1235         }
1236 
1237         /* add a right edge if we need to */
1238         if ((y > ptsIn[nextright].y + yFtrans) ||
1239             (ISEQUAL(y, ptsIn[nextright].y + yFtrans)
1240              && Marked[nextright] != 1)) {
1241             Marked[nextright]++;
1242             right = nextright--;
1243 
1244             /* find the next edge, considering the end conditions */
1245             if (nextright < 0)
1246                 nextright = count - 1;
1247 
1248             /* now compute the starting point and slope */
1249             dy = ptsIn[nextright].y - ptsIn[right].y;
1250             if (dy != 0.0) {
1251                 mr = (ptsIn[nextright].x - ptsIn[right].x) / dy;
1252                 dy = y - (ptsIn[right].y + yFtrans);
1253                 xr = (ptsIn[right].x + xFtrans) + mr * max(dy, 0);
1254             }
1255         }
1256 
1257         /*
1258          *  generate scans to fill while we still have
1259          *  a right edge as well as a left edge.
1260          */
1261         i = (min(ptsIn[nextleft].y, ptsIn[nextright].y) + yFtrans) - y;
1262 
1263         if (i < EPSILON) {
1264             if (Marked[nextleft] && Marked[nextright]) {
1265                 /* Arrgh, we're trapped! (no more points)
1266                  * Out, we've got to get out of here before this decadence saps
1267                  * our will completely! */
1268                 break;
1269             }
1270             continue;
1271         }
1272         else {
1273             j = (int) i;
1274             if (!j)
1275                 j++;
1276         }
1277         while (j > 0) {
1278             int cxl, cxr;
1279 
1280             ptsOut->y = (y) + yTrans;
1281 
1282             cxl = ICEIL(xl);
1283             cxr = ICEIL(xr);
1284             /* reverse the edges if necessary */
1285             if (xl < xr) {
1286                 *(width++) = cxr - cxl;
1287                 (ptsOut++)->x = cxl + xTrans;
1288             }
1289             else {
1290                 *(width++) = cxl - cxr;
1291                 (ptsOut++)->x = cxr + xTrans;
1292             }
1293             y++;
1294 
1295             /* increment down the edges */
1296             xl += ml;
1297             xr += mr;
1298             j--;
1299         }
1300     } while (y <= ymax);
1301 
1302     /* Finally, fill the spans we've collected */
1303     (*pgc->ops->FillSpans) (dst, pgc,
1304                             ptsOut - FirstPoint, FirstPoint, FirstWidth, 1);
1305     free(Marked);
1306     free(FirstWidth);
1307     free(FirstPoint);
1308 }
1309 static double
angleBetween(SppPointRec center,SppPointRec point1,SppPointRec point2)1310 angleBetween(SppPointRec center, SppPointRec point1, SppPointRec point2)
1311 {
1312     double a1, a2, a;
1313 
1314     /*
1315      * reflect from X coordinates back to ellipse
1316      * coordinates -- y increasing upwards
1317      */
1318     a1 = miDatan2(-(point1.y - center.y), point1.x - center.x);
1319     a2 = miDatan2(-(point2.y - center.y), point2.x - center.x);
1320     a = a2 - a1;
1321     if (a <= -180.0)
1322         a += 360.0;
1323     else if (a > 180.0)
1324         a -= 360.0;
1325     return a;
1326 }
1327 
1328 static void
translateBounds(miArcFacePtr b,int x,int y,double fx,double fy)1329 translateBounds(miArcFacePtr b, int x, int y, double fx, double fy)
1330 {
1331     fx += x;
1332     fy += y;
1333     b->clock.x -= fx;
1334     b->clock.y -= fy;
1335     b->center.x -= fx;
1336     b->center.y -= fy;
1337     b->counterClock.x -= fx;
1338     b->counterClock.y -= fy;
1339 }
1340 
1341 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)1342 miArcJoin(DrawablePtr pDraw, GCPtr pGC, miArcFacePtr pLeft,
1343           miArcFacePtr pRight, int xOrgLeft, int yOrgLeft,
1344           double xFtransLeft, double yFtransLeft,
1345           int xOrgRight, int yOrgRight,
1346           double xFtransRight, double yFtransRight)
1347 {
1348     SppPointRec center, corner, otherCorner;
1349     SppPointRec poly[5], e;
1350     SppPointPtr pArcPts;
1351     int cpt;
1352     SppArcRec arc;
1353     miArcFaceRec Right, Left;
1354     int polyLen = 0;
1355     int xOrg, yOrg;
1356     double xFtrans, yFtrans;
1357     double a;
1358     double ae, ac2, ec2, bc2, de;
1359     double width;
1360 
1361     xOrg = (xOrgRight + xOrgLeft) / 2;
1362     yOrg = (yOrgRight + yOrgLeft) / 2;
1363     xFtrans = (xFtransLeft + xFtransRight) / 2;
1364     yFtrans = (yFtransLeft + yFtransRight) / 2;
1365     Right = *pRight;
1366     translateBounds(&Right, xOrg - xOrgRight, yOrg - yOrgRight,
1367                     xFtrans - xFtransRight, yFtrans - yFtransRight);
1368     Left = *pLeft;
1369     translateBounds(&Left, xOrg - xOrgLeft, yOrg - yOrgLeft,
1370                     xFtrans - xFtransLeft, yFtrans - yFtransLeft);
1371     pRight = &Right;
1372     pLeft = &Left;
1373 
1374     if (pRight->clock.x == pLeft->counterClock.x &&
1375         pRight->clock.y == pLeft->counterClock.y)
1376         return;
1377     center = pRight->center;
1378     if (0 <= (a = angleBetween(center, pRight->clock, pLeft->counterClock))
1379         && a <= 180.0) {
1380         corner = pRight->clock;
1381         otherCorner = pLeft->counterClock;
1382     }
1383     else {
1384         a = angleBetween(center, pLeft->clock, pRight->counterClock);
1385         corner = pLeft->clock;
1386         otherCorner = pRight->counterClock;
1387     }
1388     switch (pGC->joinStyle) {
1389     case JoinRound:
1390         width = (pGC->lineWidth ? (double) pGC->lineWidth : (double) 1);
1391 
1392         arc.x = center.x - width / 2;
1393         arc.y = center.y - width / 2;
1394         arc.width = width;
1395         arc.height = width;
1396         arc.angle1 = -miDatan2(corner.y - center.y, corner.x - center.x);
1397         arc.angle2 = a;
1398         pArcPts = malloc(3 * sizeof(SppPointRec));
1399         if (!pArcPts)
1400             return;
1401         pArcPts[0].x = otherCorner.x;
1402         pArcPts[0].y = otherCorner.y;
1403         pArcPts[1].x = center.x;
1404         pArcPts[1].y = center.y;
1405         pArcPts[2].x = corner.x;
1406         pArcPts[2].y = corner.y;
1407         if ((cpt = miGetArcPts(&arc, 3, &pArcPts))) {
1408             /* by drawing with miFillSppPoly and setting the endpoints of the arc
1409              * to be the corners, we assure that the cap will meet up with the
1410              * rest of the line */
1411             miFillSppPoly(pDraw, pGC, cpt, pArcPts, xOrg, yOrg, xFtrans,
1412                           yFtrans);
1413         }
1414         free(pArcPts);
1415         return;
1416     case JoinMiter:
1417         /*
1418          * don't miter arcs with less than 11 degrees between them
1419          */
1420         if (a < 169.0) {
1421             poly[0] = corner;
1422             poly[1] = center;
1423             poly[2] = otherCorner;
1424             bc2 = (corner.x - otherCorner.x) * (corner.x - otherCorner.x) +
1425                 (corner.y - otherCorner.y) * (corner.y - otherCorner.y);
1426             ec2 = bc2 / 4;
1427             ac2 = (corner.x - center.x) * (corner.x - center.x) +
1428                 (corner.y - center.y) * (corner.y - center.y);
1429             ae = sqrt(ac2 - ec2);
1430             de = ec2 / ae;
1431             e.x = (corner.x + otherCorner.x) / 2;
1432             e.y = (corner.y + otherCorner.y) / 2;
1433             poly[3].x = e.x + de * (e.x - center.x) / ae;
1434             poly[3].y = e.y + de * (e.y - center.y) / ae;
1435             poly[4] = corner;
1436             polyLen = 5;
1437             break;
1438         }
1439     case JoinBevel:
1440         poly[0] = corner;
1441         poly[1] = center;
1442         poly[2] = otherCorner;
1443         poly[3] = corner;
1444         polyLen = 4;
1445         break;
1446     }
1447     miFillSppPoly(pDraw, pGC, polyLen, poly, xOrg, yOrg, xFtrans, yFtrans);
1448 }
1449 
1450  /*ARGSUSED*/ static void
miArcCap(DrawablePtr pDraw,GCPtr pGC,miArcFacePtr pFace,int end,int xOrg,int yOrg,double xFtrans,double yFtrans)1451 miArcCap(DrawablePtr pDraw,
1452          GCPtr pGC,
1453          miArcFacePtr pFace,
1454          int end, int xOrg, int yOrg, double xFtrans, double yFtrans)
1455 {
1456     SppPointRec corner, otherCorner, center, endPoint, poly[5];
1457 
1458     corner = pFace->clock;
1459     otherCorner = pFace->counterClock;
1460     center = pFace->center;
1461     switch (pGC->capStyle) {
1462     case CapProjecting:
1463         poly[0].x = otherCorner.x;
1464         poly[0].y = otherCorner.y;
1465         poly[1].x = corner.x;
1466         poly[1].y = corner.y;
1467         poly[2].x = corner.x - (center.y - corner.y);
1468         poly[2].y = corner.y + (center.x - corner.x);
1469         poly[3].x = otherCorner.x - (otherCorner.y - center.y);
1470         poly[3].y = otherCorner.y + (otherCorner.x - center.x);
1471         poly[4].x = otherCorner.x;
1472         poly[4].y = otherCorner.y;
1473         miFillSppPoly(pDraw, pGC, 5, poly, xOrg, yOrg, xFtrans, yFtrans);
1474         break;
1475     case CapRound:
1476         /*
1477          * miRoundCap just needs these to be unequal.
1478          */
1479         endPoint = center;
1480         endPoint.x = endPoint.x + 100;
1481         miRoundCap(pDraw, pGC, center, endPoint, corner, otherCorner, 0,
1482                    -xOrg, -yOrg, xFtrans, yFtrans);
1483         break;
1484     }
1485 }
1486 
1487 /* MIROUNDCAP -- a private helper function
1488  * Put Rounded cap on end. pCenter is the center of this end of the line
1489  * pEnd is the center of the other end of the line. pCorner is one of the
1490  * two corners at this end of the line.
1491  * NOTE:  pOtherCorner must be counter-clockwise from pCorner.
1492  */
1493  /*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)1494 miRoundCap(DrawablePtr pDraw,
1495            GCPtr pGC,
1496            SppPointRec pCenter,
1497            SppPointRec pEnd,
1498            SppPointRec pCorner,
1499            SppPointRec pOtherCorner,
1500            int fLineEnd, int xOrg, int yOrg, double xFtrans, double yFtrans)
1501 {
1502     int cpt;
1503     double width;
1504     SppArcRec arc;
1505     SppPointPtr pArcPts;
1506 
1507     width = (pGC->lineWidth ? (double) pGC->lineWidth : (double) 1);
1508 
1509     arc.x = pCenter.x - width / 2;
1510     arc.y = pCenter.y - width / 2;
1511     arc.width = width;
1512     arc.height = width;
1513     arc.angle1 = -miDatan2(pCorner.y - pCenter.y, pCorner.x - pCenter.x);
1514     if (PTISEQUAL(pCenter, pEnd))
1515         arc.angle2 = -180.0;
1516     else {
1517         arc.angle2 =
1518             -miDatan2(pOtherCorner.y - pCenter.y,
1519                       pOtherCorner.x - pCenter.x) - arc.angle1;
1520         if (arc.angle2 < 0)
1521             arc.angle2 += 360.0;
1522     }
1523     pArcPts = (SppPointPtr) NULL;
1524     if ((cpt = miGetArcPts(&arc, 0, &pArcPts))) {
1525         /* by drawing with miFillSppPoly and setting the endpoints of the arc
1526          * to be the corners, we assure that the cap will meet up with the
1527          * rest of the line */
1528         miFillSppPoly(pDraw, pGC, cpt, pArcPts, -xOrg, -yOrg, xFtrans, yFtrans);
1529     }
1530     free(pArcPts);
1531 }
1532 
1533 /*
1534  * To avoid inaccuracy at the cardinal points, use trig functions
1535  * which are exact for those angles
1536  */
1537 
1538 #ifndef M_PI
1539 #define M_PI	3.14159265358979323846
1540 #endif
1541 #ifndef M_PI_2
1542 #define M_PI_2	1.57079632679489661923
1543 #endif
1544 
1545 #define Dsin(d)	((d) == 0.0 ? 0.0 : ((d) == 90.0 ? 1.0 : sin(d*M_PI/180.0)))
1546 #define Dcos(d)	((d) == 0.0 ? 1.0 : ((d) == 90.0 ? 0.0 : cos(d*M_PI/180.0)))
1547 #define mod(a,b)	((a) >= 0 ? (a) % (b) : (b) - (-(a)) % (b))
1548 
1549 static double
miDcos(double a)1550 miDcos(double a)
1551 {
1552     int i;
1553 
1554     if (floor(a / 90) == a / 90) {
1555         i = (int) (a / 90.0);
1556         switch (mod(i, 4)) {
1557         case 0:
1558             return 1;
1559         case 1:
1560             return 0;
1561         case 2:
1562             return -1;
1563         case 3:
1564             return 0;
1565         }
1566     }
1567     return cos(a * M_PI / 180.0);
1568 }
1569 
1570 static double
miDsin(double a)1571 miDsin(double a)
1572 {
1573     int i;
1574 
1575     if (floor(a / 90) == a / 90) {
1576         i = (int) (a / 90.0);
1577         switch (mod(i, 4)) {
1578         case 0:
1579             return 0;
1580         case 1:
1581             return 1;
1582         case 2:
1583             return 0;
1584         case 3:
1585             return -1;
1586         }
1587     }
1588     return sin(a * M_PI / 180.0);
1589 }
1590 
1591 static double
miDasin(double v)1592 miDasin(double v)
1593 {
1594     if (v == 0)
1595         return 0.0;
1596     if (v == 1.0)
1597         return 90.0;
1598     if (v == -1.0)
1599         return -90.0;
1600     return asin(v) * (180.0 / M_PI);
1601 }
1602 
1603 static double
miDatan2(double dy,double dx)1604 miDatan2(double dy, double dx)
1605 {
1606     if (dy == 0) {
1607         if (dx >= 0)
1608             return 0.0;
1609         return 180.0;
1610     }
1611     else if (dx == 0) {
1612         if (dy > 0)
1613             return 90.0;
1614         return -90.0;
1615     }
1616     else if (fabs(dy) == fabs(dx)) {
1617         if (dy > 0) {
1618             if (dx > 0)
1619                 return 45.0;
1620             return 135.0;
1621         }
1622         else {
1623             if (dx > 0)
1624                 return 315.0;
1625             return 225.0;
1626         }
1627     }
1628     else {
1629         return atan2(dy, dx) * (180.0 / M_PI);
1630     }
1631 }
1632 
1633 /* MIGETARCPTS -- Converts an arc into a set of line segments -- a helper
1634  * routine for filled arc and line (round cap) code.
1635  * Returns the number of points in the arc.  Note that it takes a pointer
1636  * to a pointer to where it should put the points and an index (cpt).
1637  * This procedure allocates the space necessary to fit the arc points.
1638  * Sometimes it's convenient for those points to be at the end of an existing
1639  * array. (For example, if we want to leave a spare point to make sectors
1640  * instead of segments.)  So we pass in the malloc()ed chunk that contains the
1641  * array and an index saying where we should start stashing the points.
1642  * If there isn't an array already, we just pass in a null pointer and
1643  * count on realloc() to handle the null pointer correctly.
1644  */
1645 static int
miGetArcPts(SppArcPtr parc,int cpt,SppPointPtr * ppPts)1646 miGetArcPts(SppArcPtr parc,     /* points to an arc */
1647             int cpt,            /* number of points already in arc list */
1648             SppPointPtr * ppPts)
1649 {                               /* pointer to pointer to arc-list -- modified */
1650     double st,                  /* Start Theta, start angle */
1651      et,                        /* End Theta, offset from start theta */
1652      dt,                        /* Delta Theta, angle to sweep ellipse */
1653      cdt,                       /* Cos Delta Theta, actually 2 cos(dt) */
1654      x0, y0,                    /* the recurrence formula needs two points to start */
1655      x1, y1, x2, y2,            /* this will be the new point generated */
1656      xc, yc;                    /* the center point */
1657     int count, i;
1658     SppPointPtr poly;
1659 
1660     /* The spec says that positive angles indicate counterclockwise motion.
1661      * Given our coordinate system (with 0,0 in the upper left corner),
1662      * the screen appears flipped in Y.  The easiest fix is to negate the
1663      * angles given */
1664 
1665     st = -parc->angle1;
1666 
1667     et = -parc->angle2;
1668 
1669     /* Try to get a delta theta that is within 1/2 pixel.  Then adjust it
1670      * so that it divides evenly into the total.
1671      * I'm just using cdt 'cause I'm lazy.
1672      */
1673     cdt = parc->width;
1674     if (parc->height > cdt)
1675         cdt = parc->height;
1676     cdt /= 2.0;
1677     if (cdt <= 0)
1678         return 0;
1679     if (cdt < 1.0)
1680         cdt = 1.0;
1681     dt = miDasin(1.0 / cdt);    /* minimum step necessary */
1682     count = et / dt;
1683     count = abs(count) + 1;
1684     dt = et / count;
1685     count++;
1686 
1687     cdt = 2 * miDcos(dt);
1688     if (!(poly = reallocarray(*ppPts, cpt + count, sizeof(SppPointRec))))
1689         return 0;
1690     *ppPts = poly;
1691 
1692     xc = parc->width / 2.0;     /* store half width and half height */
1693     yc = parc->height / 2.0;
1694 
1695     x0 = xc * miDcos(st);
1696     y0 = yc * miDsin(st);
1697     x1 = xc * miDcos(st + dt);
1698     y1 = yc * miDsin(st + dt);
1699     xc += parc->x;              /* by adding initial point, these become */
1700     yc += parc->y;              /* the center point */
1701 
1702     poly[cpt].x = (xc + x0);
1703     poly[cpt].y = (yc + y0);
1704     poly[cpt + 1].x = (xc + x1);
1705     poly[cpt + 1].y = (yc + y1);
1706 
1707     for (i = 2; i < count; i++) {
1708         x2 = cdt * x1 - x0;
1709         y2 = cdt * y1 - y0;
1710 
1711         poly[cpt + i].x = (xc + x2);
1712         poly[cpt + i].y = (yc + y2);
1713 
1714         x0 = x1;
1715         y0 = y1;
1716         x1 = x2;
1717         y1 = y2;
1718     }
1719     /* adjust the last point */
1720     if (fabs(parc->angle2) >= 360.0)
1721         poly[cpt + i - 1] = poly[0];
1722     else {
1723         poly[cpt + i - 1].x = (miDcos(st + et) * parc->width / 2.0 + xc);
1724         poly[cpt + i - 1].y = (miDsin(st + et) * parc->height / 2.0 + yc);
1725     }
1726 
1727     return count;
1728 }
1729 
1730 struct arcData {
1731     double x0, y0, x1, y1;
1732     int selfJoin;
1733 };
1734 
1735 #define ADD_REALLOC_STEP	20
1736 
1737 static void
addCap(miArcCapPtr * capsp,int * ncapsp,int * sizep,int end,int arcIndex)1738 addCap(miArcCapPtr * capsp, int *ncapsp, int *sizep, int end, int arcIndex)
1739 {
1740     int newsize;
1741     miArcCapPtr cap;
1742 
1743     if (*ncapsp == *sizep) {
1744         newsize = *sizep + ADD_REALLOC_STEP;
1745         cap = reallocarray(*capsp, newsize, sizeof(**capsp));
1746         if (!cap)
1747             return;
1748         *sizep = newsize;
1749         *capsp = cap;
1750     }
1751     cap = &(*capsp)[*ncapsp];
1752     cap->end = end;
1753     cap->arcIndex = arcIndex;
1754     ++*ncapsp;
1755 }
1756 
1757 static void
addJoin(miArcJoinPtr * joinsp,int * njoinsp,int * sizep,int end0,int index0,int phase0,int end1,int index1,int phase1)1758 addJoin(miArcJoinPtr * joinsp,
1759         int *njoinsp,
1760         int *sizep,
1761         int end0, int index0, int phase0, int end1, int index1, int phase1)
1762 {
1763     int newsize;
1764     miArcJoinPtr join;
1765 
1766     if (*njoinsp == *sizep) {
1767         newsize = *sizep + ADD_REALLOC_STEP;
1768         join = reallocarray(*joinsp, newsize, sizeof(**joinsp));
1769         if (!join)
1770             return;
1771         *sizep = newsize;
1772         *joinsp = join;
1773     }
1774     join = &(*joinsp)[*njoinsp];
1775     join->end0 = end0;
1776     join->arcIndex0 = index0;
1777     join->phase0 = phase0;
1778     join->end1 = end1;
1779     join->arcIndex1 = index1;
1780     join->phase1 = phase1;
1781     ++*njoinsp;
1782 }
1783 
1784 static miArcDataPtr
addArc(miArcDataPtr * arcsp,int * narcsp,int * sizep,xArc * xarc)1785 addArc(miArcDataPtr * arcsp, int *narcsp, int *sizep, xArc * xarc)
1786 {
1787     int newsize;
1788     miArcDataPtr arc;
1789 
1790     if (*narcsp == *sizep) {
1791         newsize = *sizep + ADD_REALLOC_STEP;
1792         arc = reallocarray(*arcsp, newsize, sizeof(**arcsp));
1793         if (!arc)
1794             return NULL;
1795         *sizep = newsize;
1796         *arcsp = arc;
1797     }
1798     arc = &(*arcsp)[*narcsp];
1799     arc->arc = *xarc;
1800     ++*narcsp;
1801     return arc;
1802 }
1803 
1804 static void
miFreeArcs(miPolyArcPtr arcs,GCPtr pGC)1805 miFreeArcs(miPolyArcPtr arcs, GCPtr pGC)
1806 {
1807     int iphase;
1808 
1809     for (iphase = ((pGC->lineStyle == LineDoubleDash) ? 1 : 0);
1810          iphase >= 0; iphase--) {
1811         if (arcs[iphase].narcs > 0)
1812             free(arcs[iphase].arcs);
1813         if (arcs[iphase].njoins > 0)
1814             free(arcs[iphase].joins);
1815         if (arcs[iphase].ncaps > 0)
1816             free(arcs[iphase].caps);
1817     }
1818     free(arcs);
1819 }
1820 
1821 /*
1822  * map angles to radial distance.  This only deals with the first quadrant
1823  */
1824 
1825 /*
1826  * a polygonal approximation to the arc for computing arc lengths
1827  */
1828 
1829 #define DASH_MAP_SIZE	91
1830 
1831 #define dashIndexToAngle(di)	((((double) (di)) * 90.0) / ((double) DASH_MAP_SIZE - 1))
1832 #define xAngleToDashIndex(xa)	((((long) (xa)) * (DASH_MAP_SIZE - 1)) / (90 * 64))
1833 #define dashIndexToXAngle(di)	((((long) (di)) * (90 * 64)) / (DASH_MAP_SIZE - 1))
1834 #define dashXAngleStep	(((double) (90 * 64)) / ((double) (DASH_MAP_SIZE - 1)))
1835 
1836 typedef struct {
1837     double map[DASH_MAP_SIZE];
1838 } dashMap;
1839 
1840 static int computeAngleFromPath(int startAngle, int endAngle, dashMap * map,
1841                                 int *lenp, int backwards);
1842 
1843 static void
computeDashMap(xArc * arcp,dashMap * map)1844 computeDashMap(xArc * arcp, dashMap * map)
1845 {
1846     int di;
1847     double a, x, y, prevx = 0.0, prevy = 0.0, dist;
1848 
1849     for (di = 0; di < DASH_MAP_SIZE; di++) {
1850         a = dashIndexToAngle(di);
1851         x = ((double) arcp->width / 2.0) * miDcos(a);
1852         y = ((double) arcp->height / 2.0) * miDsin(a);
1853         if (di == 0) {
1854             map->map[di] = 0.0;
1855         }
1856         else {
1857             dist = hypot(x - prevx, y - prevy);
1858             map->map[di] = map->map[di - 1] + dist;
1859         }
1860         prevx = x;
1861         prevy = y;
1862     }
1863 }
1864 
1865 typedef enum { HORIZONTAL, VERTICAL, OTHER } arcTypes;
1866 
1867 /* this routine is a bit gory */
1868 
1869 static miPolyArcPtr
miComputeArcs(xArc * parcs,int narcs,GCPtr pGC)1870 miComputeArcs(xArc * parcs, int narcs, GCPtr pGC)
1871 {
1872     int isDashed, isDoubleDash;
1873     int dashOffset;
1874     miPolyArcPtr arcs;
1875     int start, i, j, k = 0, nexti, nextk = 0;
1876     int joinSize[2];
1877     int capSize[2];
1878     int arcSize[2];
1879     int angle2;
1880     double a0, a1;
1881     struct arcData *data;
1882     miArcDataPtr arc;
1883     xArc xarc;
1884     int iphase, prevphase = 0, joinphase;
1885     int arcsJoin;
1886     int selfJoin;
1887 
1888     int iDash = 0, dashRemaining = 0;
1889     int iDashStart = 0, dashRemainingStart = 0, iphaseStart;
1890     int startAngle, spanAngle, endAngle, backwards = 0;
1891     int prevDashAngle, dashAngle;
1892     dashMap map;
1893 
1894     isDashed = !(pGC->lineStyle == LineSolid);
1895     isDoubleDash = (pGC->lineStyle == LineDoubleDash);
1896     dashOffset = pGC->dashOffset;
1897 
1898     data = xallocarray(narcs, sizeof(struct arcData));
1899     if (!data)
1900         return NULL;
1901     arcs = xallocarray(isDoubleDash ? 2 : 1, sizeof(*arcs));
1902     if (!arcs) {
1903         free(data);
1904         return NULL;
1905     }
1906     for (i = 0; i < narcs; i++) {
1907         a0 = todeg(parcs[i].angle1);
1908         angle2 = parcs[i].angle2;
1909         if (angle2 > FULLCIRCLE)
1910             angle2 = FULLCIRCLE;
1911         else if (angle2 < -FULLCIRCLE)
1912             angle2 = -FULLCIRCLE;
1913         data[i].selfJoin = angle2 == FULLCIRCLE || angle2 == -FULLCIRCLE;
1914         a1 = todeg(parcs[i].angle1 + angle2);
1915         data[i].x0 =
1916             parcs[i].x + (double) parcs[i].width / 2 * (1 + miDcos(a0));
1917         data[i].y0 =
1918             parcs[i].y + (double) parcs[i].height / 2 * (1 - miDsin(a0));
1919         data[i].x1 =
1920             parcs[i].x + (double) parcs[i].width / 2 * (1 + miDcos(a1));
1921         data[i].y1 =
1922             parcs[i].y + (double) parcs[i].height / 2 * (1 - miDsin(a1));
1923     }
1924 
1925     for (iphase = 0; iphase < (isDoubleDash ? 2 : 1); iphase++) {
1926         arcs[iphase].njoins = 0;
1927         arcs[iphase].joins = 0;
1928         joinSize[iphase] = 0;
1929 
1930         arcs[iphase].ncaps = 0;
1931         arcs[iphase].caps = 0;
1932         capSize[iphase] = 0;
1933 
1934         arcs[iphase].narcs = 0;
1935         arcs[iphase].arcs = 0;
1936         arcSize[iphase] = 0;
1937     }
1938 
1939     iphase = 0;
1940     if (isDashed) {
1941         iDash = 0;
1942         dashRemaining = pGC->dash[0];
1943         while (dashOffset > 0) {
1944             if (dashOffset >= dashRemaining) {
1945                 dashOffset -= dashRemaining;
1946                 iphase = iphase ? 0 : 1;
1947                 iDash++;
1948                 if (iDash == pGC->numInDashList)
1949                     iDash = 0;
1950                 dashRemaining = pGC->dash[iDash];
1951             }
1952             else {
1953                 dashRemaining -= dashOffset;
1954                 dashOffset = 0;
1955             }
1956         }
1957         iDashStart = iDash;
1958         dashRemainingStart = dashRemaining;
1959     }
1960     iphaseStart = iphase;
1961 
1962     for (i = narcs - 1; i >= 0; i--) {
1963         j = i + 1;
1964         if (j == narcs)
1965             j = 0;
1966         if (data[i].selfJoin || i == j ||
1967             (UNEQUAL(data[i].x1, data[j].x0) ||
1968              UNEQUAL(data[i].y1, data[j].y0))) {
1969             if (iphase == 0 || isDoubleDash)
1970                 addCap(&arcs[iphase].caps, &arcs[iphase].ncaps,
1971                        &capSize[iphase], RIGHT_END, 0);
1972             break;
1973         }
1974     }
1975     start = i + 1;
1976     if (start == narcs)
1977         start = 0;
1978     i = start;
1979     for (;;) {
1980         j = i + 1;
1981         if (j == narcs)
1982             j = 0;
1983         nexti = i + 1;
1984         if (nexti == narcs)
1985             nexti = 0;
1986         if (isDashed) {
1987             /*
1988              ** deal with dashed arcs.  Use special rules for certain 0 area arcs.
1989              ** Presumably, the other 0 area arcs still aren't done right.
1990              */
1991             arcTypes arcType = OTHER;
1992             CARD16 thisLength;
1993 
1994             if (parcs[i].height == 0
1995                 && (parcs[i].angle1 % FULLCIRCLE) == 0x2d00
1996                 && parcs[i].angle2 == 0x2d00)
1997                 arcType = HORIZONTAL;
1998             else if (parcs[i].width == 0
1999                      && (parcs[i].angle1 % FULLCIRCLE) == 0x1680
2000                      && parcs[i].angle2 == 0x2d00)
2001                 arcType = VERTICAL;
2002             if (arcType == OTHER) {
2003                 /*
2004                  * precompute an approximation map
2005                  */
2006                 computeDashMap(&parcs[i], &map);
2007                 /*
2008                  * compute each individual dash segment using the path
2009                  * length function
2010                  */
2011                 startAngle = parcs[i].angle1;
2012                 spanAngle = parcs[i].angle2;
2013                 if (spanAngle > FULLCIRCLE)
2014                     spanAngle = FULLCIRCLE;
2015                 else if (spanAngle < -FULLCIRCLE)
2016                     spanAngle = -FULLCIRCLE;
2017                 if (startAngle < 0)
2018                     startAngle = FULLCIRCLE - (-startAngle) % FULLCIRCLE;
2019                 if (startAngle >= FULLCIRCLE)
2020                     startAngle = startAngle % FULLCIRCLE;
2021                 endAngle = startAngle + spanAngle;
2022                 backwards = spanAngle < 0;
2023             }
2024             else {
2025                 xarc = parcs[i];
2026                 if (arcType == VERTICAL) {
2027                     xarc.angle1 = 0x1680;
2028                     startAngle = parcs[i].y;
2029                     endAngle = startAngle + parcs[i].height;
2030                 }
2031                 else {
2032                     xarc.angle1 = 0x2d00;
2033                     startAngle = parcs[i].x;
2034                     endAngle = startAngle + parcs[i].width;
2035                 }
2036             }
2037             dashAngle = startAngle;
2038             selfJoin = data[i].selfJoin && (iphase == 0 || isDoubleDash);
2039             /*
2040              * add dashed arcs to each bucket
2041              */
2042             arc = 0;
2043             while (dashAngle != endAngle) {
2044                 prevDashAngle = dashAngle;
2045                 if (arcType == OTHER) {
2046                     dashAngle = computeAngleFromPath(prevDashAngle, endAngle,
2047                                                      &map, &dashRemaining,
2048                                                      backwards);
2049                     /* avoid troubles with huge arcs and small dashes */
2050                     if (dashAngle == prevDashAngle) {
2051                         if (backwards)
2052                             dashAngle--;
2053                         else
2054                             dashAngle++;
2055                     }
2056                 }
2057                 else {
2058                     thisLength = (dashAngle + dashRemaining <= endAngle) ?
2059                         dashRemaining : endAngle - dashAngle;
2060                     if (arcType == VERTICAL) {
2061                         xarc.y = dashAngle;
2062                         xarc.height = thisLength;
2063                     }
2064                     else {
2065                         xarc.x = dashAngle;
2066                         xarc.width = thisLength;
2067                     }
2068                     dashAngle += thisLength;
2069                     dashRemaining -= thisLength;
2070                 }
2071                 if (iphase == 0 || isDoubleDash) {
2072                     if (arcType == OTHER) {
2073                         xarc = parcs[i];
2074                         spanAngle = prevDashAngle;
2075                         if (spanAngle < 0)
2076                             spanAngle = FULLCIRCLE - (-spanAngle) % FULLCIRCLE;
2077                         if (spanAngle >= FULLCIRCLE)
2078                             spanAngle = spanAngle % FULLCIRCLE;
2079                         xarc.angle1 = spanAngle;
2080                         spanAngle = dashAngle - prevDashAngle;
2081                         if (backwards) {
2082                             if (dashAngle > prevDashAngle)
2083                                 spanAngle = -FULLCIRCLE + spanAngle;
2084                         }
2085                         else {
2086                             if (dashAngle < prevDashAngle)
2087                                 spanAngle = FULLCIRCLE + spanAngle;
2088                         }
2089                         if (spanAngle > FULLCIRCLE)
2090                             spanAngle = FULLCIRCLE;
2091                         if (spanAngle < -FULLCIRCLE)
2092                             spanAngle = -FULLCIRCLE;
2093                         xarc.angle2 = spanAngle;
2094                     }
2095                     arc = addArc(&arcs[iphase].arcs, &arcs[iphase].narcs,
2096                                  &arcSize[iphase], &xarc);
2097                     if (!arc)
2098                         goto arcfail;
2099                     /*
2100                      * cap each end of an on/off dash
2101                      */
2102                     if (!isDoubleDash) {
2103                         if (prevDashAngle != startAngle) {
2104                             addCap(&arcs[iphase].caps,
2105                                    &arcs[iphase].ncaps,
2106                                    &capSize[iphase], RIGHT_END,
2107                                    arc - arcs[iphase].arcs);
2108 
2109                         }
2110                         if (dashAngle != endAngle) {
2111                             addCap(&arcs[iphase].caps,
2112                                    &arcs[iphase].ncaps,
2113                                    &capSize[iphase], LEFT_END,
2114                                    arc - arcs[iphase].arcs);
2115                         }
2116                     }
2117                     arc->cap = arcs[iphase].ncaps;
2118                     arc->join = arcs[iphase].njoins;
2119                     arc->render = 0;
2120                     arc->selfJoin = 0;
2121                     if (dashAngle == endAngle)
2122                         arc->selfJoin = selfJoin;
2123                 }
2124                 prevphase = iphase;
2125                 if (dashRemaining <= 0) {
2126                     ++iDash;
2127                     if (iDash == pGC->numInDashList)
2128                         iDash = 0;
2129                     iphase = iphase ? 0 : 1;
2130                     dashRemaining = pGC->dash[iDash];
2131                 }
2132             }
2133             /*
2134              * make sure a place exists for the position data when
2135              * drawing a zero-length arc
2136              */
2137             if (startAngle == endAngle) {
2138                 prevphase = iphase;
2139                 if (!isDoubleDash && iphase == 1)
2140                     prevphase = 0;
2141                 arc = addArc(&arcs[prevphase].arcs, &arcs[prevphase].narcs,
2142                              &arcSize[prevphase], &parcs[i]);
2143                 if (!arc)
2144                     goto arcfail;
2145                 arc->join = arcs[prevphase].njoins;
2146                 arc->cap = arcs[prevphase].ncaps;
2147                 arc->selfJoin = data[i].selfJoin;
2148             }
2149         }
2150         else {
2151             arc = addArc(&arcs[iphase].arcs, &arcs[iphase].narcs,
2152                          &arcSize[iphase], &parcs[i]);
2153             if (!arc)
2154                 goto arcfail;
2155             arc->join = arcs[iphase].njoins;
2156             arc->cap = arcs[iphase].ncaps;
2157             arc->selfJoin = data[i].selfJoin;
2158             prevphase = iphase;
2159         }
2160         if (prevphase == 0 || isDoubleDash)
2161             k = arcs[prevphase].narcs - 1;
2162         if (iphase == 0 || isDoubleDash)
2163             nextk = arcs[iphase].narcs;
2164         if (nexti == start) {
2165             nextk = 0;
2166             if (isDashed) {
2167                 iDash = iDashStart;
2168                 iphase = iphaseStart;
2169                 dashRemaining = dashRemainingStart;
2170             }
2171         }
2172         arcsJoin = narcs > 1 && i != j &&
2173             ISEQUAL(data[i].x1, data[j].x0) &&
2174             ISEQUAL(data[i].y1, data[j].y0) &&
2175             !data[i].selfJoin && !data[j].selfJoin;
2176         if (arc) {
2177             if (arcsJoin)
2178                 arc->render = 0;
2179             else
2180                 arc->render = 1;
2181         }
2182         if (arcsJoin &&
2183             (prevphase == 0 || isDoubleDash) && (iphase == 0 || isDoubleDash)) {
2184             joinphase = iphase;
2185             if (isDoubleDash) {
2186                 if (nexti == start)
2187                     joinphase = iphaseStart;
2188                 /*
2189                  * if the join is right at the dash,
2190                  * draw the join in foreground
2191                  * This is because the foreground
2192                  * arcs are computed second, the results
2193                  * of which are needed to draw the join
2194                  */
2195                 if (joinphase != prevphase)
2196                     joinphase = 0;
2197             }
2198             if (joinphase == 0 || isDoubleDash) {
2199                 addJoin(&arcs[joinphase].joins,
2200                         &arcs[joinphase].njoins,
2201                         &joinSize[joinphase],
2202                         LEFT_END, k, prevphase, RIGHT_END, nextk, iphase);
2203                 arc->join = arcs[prevphase].njoins;
2204             }
2205         }
2206         else {
2207             /*
2208              * cap the left end of this arc
2209              * unless it joins itself
2210              */
2211             if ((prevphase == 0 || isDoubleDash) && !arc->selfJoin) {
2212                 addCap(&arcs[prevphase].caps, &arcs[prevphase].ncaps,
2213                        &capSize[prevphase], LEFT_END, k);
2214                 arc->cap = arcs[prevphase].ncaps;
2215             }
2216             if (isDashed && !arcsJoin) {
2217                 iDash = iDashStart;
2218                 iphase = iphaseStart;
2219                 dashRemaining = dashRemainingStart;
2220             }
2221             nextk = arcs[iphase].narcs;
2222             if (nexti == start) {
2223                 nextk = 0;
2224                 iDash = iDashStart;
2225                 iphase = iphaseStart;
2226                 dashRemaining = dashRemainingStart;
2227             }
2228             /*
2229              * cap the right end of the next arc.  If the
2230              * next arc is actually the first arc, only
2231              * cap it if it joins with this arc.  This
2232              * case will occur when the final dash segment
2233              * of an on/off dash is off.  Of course, this
2234              * cap will be drawn at a strange time, but that
2235              * hardly matters...
2236              */
2237             if ((iphase == 0 || isDoubleDash) &&
2238                 (nexti != start || (arcsJoin && isDashed)))
2239                 addCap(&arcs[iphase].caps, &arcs[iphase].ncaps,
2240                        &capSize[iphase], RIGHT_END, nextk);
2241         }
2242         i = nexti;
2243         if (i == start)
2244             break;
2245     }
2246     /*
2247      * make sure the last section is rendered
2248      */
2249     for (iphase = 0; iphase < (isDoubleDash ? 2 : 1); iphase++)
2250         if (arcs[iphase].narcs > 0) {
2251             arcs[iphase].arcs[arcs[iphase].narcs - 1].render = 1;
2252             arcs[iphase].arcs[arcs[iphase].narcs - 1].join =
2253                 arcs[iphase].njoins;
2254             arcs[iphase].arcs[arcs[iphase].narcs - 1].cap = arcs[iphase].ncaps;
2255         }
2256     free(data);
2257     return arcs;
2258  arcfail:
2259     miFreeArcs(arcs, pGC);
2260     free(data);
2261     return NULL;
2262 }
2263 
2264 static double
angleToLength(int angle,dashMap * map)2265 angleToLength(int angle, dashMap * map)
2266 {
2267     double len, excesslen, sidelen = map->map[DASH_MAP_SIZE - 1], totallen;
2268     int di;
2269     int excess;
2270     Bool oddSide = FALSE;
2271 
2272     totallen = 0;
2273     if (angle >= 0) {
2274         while (angle >= 90 * 64) {
2275             angle -= 90 * 64;
2276             totallen += sidelen;
2277             oddSide = !oddSide;
2278         }
2279     }
2280     else {
2281         while (angle < 0) {
2282             angle += 90 * 64;
2283             totallen -= sidelen;
2284             oddSide = !oddSide;
2285         }
2286     }
2287     if (oddSide)
2288         angle = 90 * 64 - angle;
2289 
2290     di = xAngleToDashIndex(angle);
2291     excess = angle - dashIndexToXAngle(di);
2292 
2293     len = map->map[di];
2294     /*
2295      * linearly interpolate between this point and the next
2296      */
2297     if (excess > 0) {
2298         excesslen = (map->map[di + 1] - map->map[di]) *
2299             ((double) excess) / dashXAngleStep;
2300         len += excesslen;
2301     }
2302     if (oddSide)
2303         totallen += (sidelen - len);
2304     else
2305         totallen += len;
2306     return totallen;
2307 }
2308 
2309 /*
2310  * len is along the arc, but may be more than one rotation
2311  */
2312 
2313 static int
lengthToAngle(double len,dashMap * map)2314 lengthToAngle(double len, dashMap * map)
2315 {
2316     double sidelen = map->map[DASH_MAP_SIZE - 1];
2317     int angle, angleexcess;
2318     Bool oddSide = FALSE;
2319     int a0, a1, a;
2320 
2321     angle = 0;
2322     /*
2323      * step around the ellipse, subtracting sidelens and
2324      * adding 90 degrees.  oddSide will tell if the
2325      * map should be interpolated in reverse
2326      */
2327     if (len >= 0) {
2328         if (sidelen == 0)
2329             return 2 * FULLCIRCLE;      /* infinity */
2330         while (len >= sidelen) {
2331             angle += 90 * 64;
2332             len -= sidelen;
2333             oddSide = !oddSide;
2334         }
2335     }
2336     else {
2337         if (sidelen == 0)
2338             return -2 * FULLCIRCLE;     /* infinity */
2339         while (len < 0) {
2340             angle -= 90 * 64;
2341             len += sidelen;
2342             oddSide = !oddSide;
2343         }
2344     }
2345     if (oddSide)
2346         len = sidelen - len;
2347     a0 = 0;
2348     a1 = DASH_MAP_SIZE - 1;
2349     /*
2350      * binary search for the closest pre-computed length
2351      */
2352     while (a1 - a0 > 1) {
2353         a = (a0 + a1) / 2;
2354         if (len > map->map[a])
2355             a0 = a;
2356         else
2357             a1 = a;
2358     }
2359     angleexcess = dashIndexToXAngle(a0);
2360     /*
2361      * linearly interpolate to the next point
2362      */
2363     angleexcess += (len - map->map[a0]) /
2364         (map->map[a0 + 1] - map->map[a0]) * dashXAngleStep;
2365     if (oddSide)
2366         angle += (90 * 64) - angleexcess;
2367     else
2368         angle += angleexcess;
2369     return angle;
2370 }
2371 
2372 /*
2373  * compute the angle of an ellipse which corresponds to
2374  * the given path length.  Note that the correct solution
2375  * to this problem is an eliptic integral, we'll punt and
2376  * approximate (it's only for dashes anyway).  This
2377  * approximation uses a polygon.
2378  *
2379  * The remaining portion of len is stored in *lenp -
2380  * this will be negative if the arc extends beyond
2381  * len and positive if len extends beyond the arc.
2382  */
2383 
2384 static int
computeAngleFromPath(int startAngle,int endAngle,dashMap * map,int * lenp,int backwards)2385 computeAngleFromPath(int startAngle, int endAngle,      /* normalized absolute angles in *64 degrees */
2386                      dashMap * map, int *lenp, int backwards)
2387 {
2388     int a0, a1, a;
2389     double len0;
2390     int len;
2391 
2392     a0 = startAngle;
2393     a1 = endAngle;
2394     len = *lenp;
2395     if (backwards) {
2396         /*
2397          * flip the problem around to always be
2398          * forwards
2399          */
2400         a0 = FULLCIRCLE - a0;
2401         a1 = FULLCIRCLE - a1;
2402     }
2403     if (a1 < a0)
2404         a1 += FULLCIRCLE;
2405     len0 = angleToLength(a0, map);
2406     a = lengthToAngle(len0 + len, map);
2407     if (a > a1) {
2408         a = a1;
2409         len -= angleToLength(a1, map) - len0;
2410     }
2411     else
2412         len = 0;
2413     if (backwards)
2414         a = FULLCIRCLE - a;
2415     *lenp = len;
2416     return a;
2417 }
2418 
2419 /*
2420  * scan convert wide arcs.
2421  */
2422 
2423 /*
2424  * draw zero width/height arcs
2425  */
2426 
2427 static void
drawZeroArc(DrawablePtr pDraw,GCPtr pGC,xArc * tarc,int lw,miArcFacePtr left,miArcFacePtr right)2428 drawZeroArc(DrawablePtr pDraw,
2429             GCPtr pGC,
2430             xArc * tarc, int lw, miArcFacePtr left, miArcFacePtr right)
2431 {
2432     double x0 = 0.0, y0 = 0.0, x1 = 0.0, y1 = 0.0, w, h, x, y;
2433     double xmax, ymax, xmin, ymin;
2434     int a0, a1;
2435     double a, startAngle, endAngle;
2436     double l, lx, ly;
2437 
2438     l = lw / 2.0;
2439     a0 = tarc->angle1;
2440     a1 = tarc->angle2;
2441     if (a1 > FULLCIRCLE)
2442         a1 = FULLCIRCLE;
2443     else if (a1 < -FULLCIRCLE)
2444         a1 = -FULLCIRCLE;
2445     w = (double) tarc->width / 2.0;
2446     h = (double) tarc->height / 2.0;
2447     /*
2448      * play in X coordinates right away
2449      */
2450     startAngle = -((double) a0 / 64.0);
2451     endAngle = -((double) (a0 + a1) / 64.0);
2452 
2453     xmax = -w;
2454     xmin = w;
2455     ymax = -h;
2456     ymin = h;
2457     a = startAngle;
2458     for (;;) {
2459         x = w * miDcos(a);
2460         y = h * miDsin(a);
2461         if (a == startAngle) {
2462             x0 = x;
2463             y0 = y;
2464         }
2465         if (a == endAngle) {
2466             x1 = x;
2467             y1 = y;
2468         }
2469         if (x > xmax)
2470             xmax = x;
2471         if (x < xmin)
2472             xmin = x;
2473         if (y > ymax)
2474             ymax = y;
2475         if (y < ymin)
2476             ymin = y;
2477         if (a == endAngle)
2478             break;
2479         if (a1 < 0) {           /* clockwise */
2480             if (floor(a / 90.0) == floor(endAngle / 90.0))
2481                 a = endAngle;
2482             else
2483                 a = 90 * (floor(a / 90.0) + 1);
2484         }
2485         else {
2486             if (ceil(a / 90.0) == ceil(endAngle / 90.0))
2487                 a = endAngle;
2488             else
2489                 a = 90 * (ceil(a / 90.0) - 1);
2490         }
2491     }
2492     lx = ly = l;
2493     if ((x1 - x0) + (y1 - y0) < 0)
2494         lx = ly = -l;
2495     if (h) {
2496         ly = 0.0;
2497         lx = -lx;
2498     }
2499     else
2500         lx = 0.0;
2501     if (right) {
2502         right->center.x = x0;
2503         right->center.y = y0;
2504         right->clock.x = x0 - lx;
2505         right->clock.y = y0 - ly;
2506         right->counterClock.x = x0 + lx;
2507         right->counterClock.y = y0 + ly;
2508     }
2509     if (left) {
2510         left->center.x = x1;
2511         left->center.y = y1;
2512         left->clock.x = x1 + lx;
2513         left->clock.y = y1 + ly;
2514         left->counterClock.x = x1 - lx;
2515         left->counterClock.y = y1 - ly;
2516     }
2517 
2518     x0 = xmin;
2519     x1 = xmax;
2520     y0 = ymin;
2521     y1 = ymax;
2522     if (ymin != y1) {
2523         xmin = -l;
2524         xmax = l;
2525     }
2526     else {
2527         ymin = -l;
2528         ymax = l;
2529     }
2530     if (xmax != xmin && ymax != ymin) {
2531         int minx, maxx, miny, maxy;
2532         xRectangle rect;
2533 
2534         minx = ICEIL(xmin + w) + tarc->x;
2535         maxx = ICEIL(xmax + w) + tarc->x;
2536         miny = ICEIL(ymin + h) + tarc->y;
2537         maxy = ICEIL(ymax + h) + tarc->y;
2538         rect.x = minx;
2539         rect.y = miny;
2540         rect.width = maxx - minx;
2541         rect.height = maxy - miny;
2542         (*pGC->ops->PolyFillRect) (pDraw, pGC, 1, &rect);
2543     }
2544 }
2545 
2546 /*
2547  * this computes the ellipse y value associated with the
2548  * bottom of the tail.
2549  */
2550 
2551 static void
tailEllipseY(struct arc_def * def,struct accelerators * acc)2552 tailEllipseY(struct arc_def *def, struct accelerators *acc)
2553 {
2554     double t;
2555 
2556     acc->tail_y = 0.0;
2557     if (def->w == def->h)
2558         return;
2559     t = def->l * def->w;
2560     if (def->w > def->h) {
2561         if (t < acc->h2)
2562             return;
2563     }
2564     else {
2565         if (t > acc->h2)
2566             return;
2567     }
2568     t = 2.0 * def->h * t;
2569     t = (CUBED_ROOT_4 * acc->h2 - cbrt(t * t)) / acc->h2mw2;
2570     if (t > 0.0)
2571         acc->tail_y = def->h / CUBED_ROOT_2 * sqrt(t);
2572 }
2573 
2574 /*
2575  * inverse functions -- compute edge coordinates
2576  * from the ellipse
2577  */
2578 
2579 static double
outerXfromXY(double x,double y,struct arc_def * def,struct accelerators * acc)2580 outerXfromXY(double x, double y, struct arc_def *def, struct accelerators *acc)
2581 {
2582     return x + (x * acc->h2l) / sqrt(x * x * acc->h4 + y * y * acc->w4);
2583 }
2584 
2585 static double
outerYfromXY(double x,double y,struct arc_def * def,struct accelerators * acc)2586 outerYfromXY(double x, double y, struct arc_def *def, struct accelerators *acc)
2587 {
2588     return y + (y * acc->w2l) / sqrt(x * x * acc->h4 + y * y * acc->w4);
2589 }
2590 
2591 static double
innerXfromXY(double x,double y,struct arc_def * def,struct accelerators * acc)2592 innerXfromXY(double x, double y, struct arc_def *def, struct accelerators *acc)
2593 {
2594     return x - (x * acc->h2l) / sqrt(x * x * acc->h4 + y * y * acc->w4);
2595 }
2596 
2597 static double
innerYfromXY(double x,double y,struct arc_def * def,struct accelerators * acc)2598 innerYfromXY(double x, double y, struct arc_def *def, struct accelerators *acc)
2599 {
2600     return y - (y * acc->w2l) / sqrt(x * x * acc->h4 + y * y * acc->w4);
2601 }
2602 
2603 static double
innerYfromY(double y,struct arc_def * def,struct accelerators * acc)2604 innerYfromY(double y, struct arc_def *def, struct accelerators *acc)
2605 {
2606     double x;
2607 
2608     x = (def->w / def->h) * sqrt(acc->h2 - y * y);
2609 
2610     return y - (y * acc->w2l) / sqrt(x * x * acc->h4 + y * y * acc->w4);
2611 }
2612 
2613 static void
computeLine(double x1,double y1,double x2,double y2,struct line * line)2614 computeLine(double x1, double y1, double x2, double y2, struct line *line)
2615 {
2616     if (y1 == y2)
2617         line->valid = 0;
2618     else {
2619         line->m = (x1 - x2) / (y1 - y2);
2620         line->b = x1 - y1 * line->m;
2621         line->valid = 1;
2622     }
2623 }
2624 
2625 /*
2626  * compute various accelerators for an ellipse.  These
2627  * are simply values that are used repeatedly in
2628  * the computations
2629  */
2630 
2631 static void
computeAcc(xArc * tarc,int lw,struct arc_def * def,struct accelerators * acc)2632 computeAcc(xArc * tarc, int lw, struct arc_def *def, struct accelerators *acc)
2633 {
2634     def->w = ((double) tarc->width) / 2.0;
2635     def->h = ((double) tarc->height) / 2.0;
2636     def->l = ((double) lw) / 2.0;
2637     acc->h2 = def->h * def->h;
2638     acc->w2 = def->w * def->w;
2639     acc->h4 = acc->h2 * acc->h2;
2640     acc->w4 = acc->w2 * acc->w2;
2641     acc->h2l = acc->h2 * def->l;
2642     acc->w2l = acc->w2 * def->l;
2643     acc->h2mw2 = acc->h2 - acc->w2;
2644     acc->fromIntX = (tarc->width & 1) ? 0.5 : 0.0;
2645     acc->fromIntY = (tarc->height & 1) ? 0.5 : 0.0;
2646     acc->xorg = tarc->x + (tarc->width >> 1);
2647     acc->yorgu = tarc->y + (tarc->height >> 1);
2648     acc->yorgl = acc->yorgu + (tarc->height & 1);
2649     tailEllipseY(def, acc);
2650 }
2651 
2652 /*
2653  * compute y value bounds of various portions of the arc,
2654  * the outer edge, the ellipse and the inner edge.
2655  */
2656 
2657 static void
computeBound(struct arc_def * def,struct arc_bound * bound,struct accelerators * acc,miArcFacePtr right,miArcFacePtr left)2658 computeBound(struct arc_def *def,
2659              struct arc_bound *bound,
2660              struct accelerators *acc, miArcFacePtr right, miArcFacePtr left)
2661 {
2662     double t;
2663     double innerTaily;
2664     double tail_y;
2665     struct bound innerx, outerx;
2666     struct bound ellipsex;
2667 
2668     bound->ellipse.min = Dsin(def->a0) * def->h;
2669     bound->ellipse.max = Dsin(def->a1) * def->h;
2670     if (def->a0 == 45 && def->w == def->h)
2671         ellipsex.min = bound->ellipse.min;
2672     else
2673         ellipsex.min = Dcos(def->a0) * def->w;
2674     if (def->a1 == 45 && def->w == def->h)
2675         ellipsex.max = bound->ellipse.max;
2676     else
2677         ellipsex.max = Dcos(def->a1) * def->w;
2678     bound->outer.min = outerYfromXY(ellipsex.min, bound->ellipse.min, def, acc);
2679     bound->outer.max = outerYfromXY(ellipsex.max, bound->ellipse.max, def, acc);
2680     bound->inner.min = innerYfromXY(ellipsex.min, bound->ellipse.min, def, acc);
2681     bound->inner.max = innerYfromXY(ellipsex.max, bound->ellipse.max, def, acc);
2682 
2683     outerx.min = outerXfromXY(ellipsex.min, bound->ellipse.min, def, acc);
2684     outerx.max = outerXfromXY(ellipsex.max, bound->ellipse.max, def, acc);
2685     innerx.min = innerXfromXY(ellipsex.min, bound->ellipse.min, def, acc);
2686     innerx.max = innerXfromXY(ellipsex.max, bound->ellipse.max, def, acc);
2687 
2688     /*
2689      * save the line end points for the
2690      * cap code to use.  Careful here, these are
2691      * in cartesean coordinates (y increasing upwards)
2692      * while the cap code uses inverted coordinates
2693      * (y increasing downwards)
2694      */
2695 
2696     if (right) {
2697         right->counterClock.y = bound->outer.min;
2698         right->counterClock.x = outerx.min;
2699         right->center.y = bound->ellipse.min;
2700         right->center.x = ellipsex.min;
2701         right->clock.y = bound->inner.min;
2702         right->clock.x = innerx.min;
2703     }
2704 
2705     if (left) {
2706         left->clock.y = bound->outer.max;
2707         left->clock.x = outerx.max;
2708         left->center.y = bound->ellipse.max;
2709         left->center.x = ellipsex.max;
2710         left->counterClock.y = bound->inner.max;
2711         left->counterClock.x = innerx.max;
2712     }
2713 
2714     bound->left.min = bound->inner.max;
2715     bound->left.max = bound->outer.max;
2716     bound->right.min = bound->inner.min;
2717     bound->right.max = bound->outer.min;
2718 
2719     computeLine(innerx.min, bound->inner.min, outerx.min, bound->outer.min,
2720                 &acc->right);
2721     computeLine(innerx.max, bound->inner.max, outerx.max, bound->outer.max,
2722                 &acc->left);
2723 
2724     if (bound->inner.min > bound->inner.max) {
2725         t = bound->inner.min;
2726         bound->inner.min = bound->inner.max;
2727         bound->inner.max = t;
2728     }
2729     tail_y = acc->tail_y;
2730     if (tail_y > bound->ellipse.max)
2731         tail_y = bound->ellipse.max;
2732     else if (tail_y < bound->ellipse.min)
2733         tail_y = bound->ellipse.min;
2734     innerTaily = innerYfromY(tail_y, def, acc);
2735     if (bound->inner.min > innerTaily)
2736         bound->inner.min = innerTaily;
2737     if (bound->inner.max < innerTaily)
2738         bound->inner.max = innerTaily;
2739     bound->inneri.min = ICEIL(bound->inner.min - acc->fromIntY);
2740     bound->inneri.max = floor(bound->inner.max - acc->fromIntY);
2741     bound->outeri.min = ICEIL(bound->outer.min - acc->fromIntY);
2742     bound->outeri.max = floor(bound->outer.max - acc->fromIntY);
2743 }
2744 
2745 /*
2746  * this section computes the x value of the span at y
2747  * intersected with the specified face of the ellipse.
2748  *
2749  * this is the min/max X value over the set of normal
2750  * lines to the entire ellipse,  the equation of the
2751  * normal lines is:
2752  *
2753  *     ellipse_x h^2                   h^2
2754  * x = ------------ y + ellipse_x (1 - --- )
2755  *     ellipse_y w^2                   w^2
2756  *
2757  * compute the derivative with-respect-to ellipse_y and solve
2758  * for zero:
2759  *
2760  *       (w^2 - h^2) ellipse_y^3 + h^4 y
2761  * 0 = - ----------------------------------
2762  *       h w ellipse_y^2 sqrt (h^2 - ellipse_y^2)
2763  *
2764  *             (   h^4 y     )
2765  * ellipse_y = ( ----------  ) ^ (1/3)
2766  *             ( (h^2 - w^2) )
2767  *
2768  * The other two solutions to the equation are imaginary.
2769  *
2770  * This gives the position on the ellipse which generates
2771  * the normal with the largest/smallest x intersection point.
2772  *
2773  * Now compute the second derivative to check whether
2774  * the intersection is a minimum or maximum:
2775  *
2776  *    h (y0^3 (w^2 - h^2) + h^2 y (3y0^2 - 2h^2))
2777  * -  -------------------------------------------
2778  *          w y0^3 (sqrt (h^2 - y^2)) ^ 3
2779  *
2780  * as we only care about the sign,
2781  *
2782  * - (y0^3 (w^2 - h^2) + h^2 y (3y0^2 - 2h^2))
2783  *
2784  * or (to use accelerators),
2785  *
2786  * y0^3 (h^2 - w^2) - h^2 y (3y0^2 - 2h^2)
2787  *
2788  */
2789 
2790 /*
2791  * computes the position on the ellipse whose normal line
2792  * intersects the given scan line maximally
2793  */
2794 
2795 static double
hookEllipseY(double scan_y,struct arc_bound * bound,struct accelerators * acc,int left)2796 hookEllipseY(double scan_y,
2797              struct arc_bound *bound, struct accelerators *acc, int left)
2798 {
2799     double ret;
2800 
2801     if (acc->h2mw2 == 0) {
2802         if ((scan_y > 0 && !left) || (scan_y < 0 && left))
2803             return bound->ellipse.min;
2804         return bound->ellipse.max;
2805     }
2806     ret = (acc->h4 * scan_y) / (acc->h2mw2);
2807     if (ret >= 0)
2808         return cbrt(ret);
2809     else
2810         return -cbrt(-ret);
2811 }
2812 
2813 /*
2814  * computes the X value of the intersection of the
2815  * given scan line with the right side of the lower hook
2816  */
2817 
2818 static double
hookX(double scan_y,struct arc_def * def,struct arc_bound * bound,struct accelerators * acc,int left)2819 hookX(double scan_y,
2820       struct arc_def *def,
2821       struct arc_bound *bound, struct accelerators *acc, int left)
2822 {
2823     double ellipse_y, x;
2824     double maxMin;
2825 
2826     if (def->w != def->h) {
2827         ellipse_y = hookEllipseY(scan_y, bound, acc, left);
2828         if (boundedLe(ellipse_y, bound->ellipse)) {
2829             /*
2830              * compute the value of the second
2831              * derivative
2832              */
2833             maxMin = ellipse_y * ellipse_y * ellipse_y * acc->h2mw2 -
2834                 acc->h2 * scan_y * (3 * ellipse_y * ellipse_y - 2 * acc->h2);
2835             if ((left && maxMin > 0) || (!left && maxMin < 0)) {
2836                 if (ellipse_y == 0)
2837                     return def->w + left ? -def->l : def->l;
2838                 x = (acc->h2 * scan_y - ellipse_y * acc->h2mw2) *
2839                     sqrt(acc->h2 - ellipse_y * ellipse_y) /
2840                     (def->h * def->w * ellipse_y);
2841                 return x;
2842             }
2843         }
2844     }
2845     if (left) {
2846         if (acc->left.valid && boundedLe(scan_y, bound->left)) {
2847             x = intersectLine(scan_y, acc->left);
2848         }
2849         else {
2850             if (acc->right.valid)
2851                 x = intersectLine(scan_y, acc->right);
2852             else
2853                 x = def->w - def->l;
2854         }
2855     }
2856     else {
2857         if (acc->right.valid && boundedLe(scan_y, bound->right)) {
2858             x = intersectLine(scan_y, acc->right);
2859         }
2860         else {
2861             if (acc->left.valid)
2862                 x = intersectLine(scan_y, acc->left);
2863             else
2864                 x = def->w - def->l;
2865         }
2866     }
2867     return x;
2868 }
2869 
2870 /*
2871  * generate the set of spans with
2872  * the given y coordinate
2873  */
2874 
2875 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)2876 arcSpan(int y,
2877         int lx,
2878         int lw,
2879         int rx,
2880         int rw,
2881         struct arc_def *def,
2882         struct arc_bound *bounds, struct accelerators *acc, int mask)
2883 {
2884     int linx, loutx, rinx, routx;
2885     double x, altx;
2886 
2887     if (boundedLe(y, bounds->inneri)) {
2888         linx = -(lx + lw);
2889         rinx = rx;
2890     }
2891     else {
2892         /*
2893          * intersection with left face
2894          */
2895         x = hookX(y + acc->fromIntY, def, bounds, acc, 1);
2896         if (acc->right.valid && boundedLe(y + acc->fromIntY, bounds->right)) {
2897             altx = intersectLine(y + acc->fromIntY, acc->right);
2898             if (altx < x)
2899                 x = altx;
2900         }
2901         linx = -ICEIL(acc->fromIntX - x);
2902         rinx = ICEIL(acc->fromIntX + x);
2903     }
2904     if (boundedLe(y, bounds->outeri)) {
2905         loutx = -lx;
2906         routx = rx + rw;
2907     }
2908     else {
2909         /*
2910          * intersection with right face
2911          */
2912         x = hookX(y + acc->fromIntY, def, bounds, acc, 0);
2913         if (acc->left.valid && boundedLe(y + acc->fromIntY, bounds->left)) {
2914             altx = x;
2915             x = intersectLine(y + acc->fromIntY, acc->left);
2916             if (x < altx)
2917                 x = altx;
2918         }
2919         loutx = -ICEIL(acc->fromIntX - x);
2920         routx = ICEIL(acc->fromIntX + x);
2921     }
2922     if (routx > rinx) {
2923         if (mask & 1)
2924             newFinalSpan(acc->yorgu - y, acc->xorg + rinx, acc->xorg + routx);
2925         if (mask & 8)
2926             newFinalSpan(acc->yorgl + y, acc->xorg + rinx, acc->xorg + routx);
2927     }
2928     if (loutx > linx) {
2929         if (mask & 2)
2930             newFinalSpan(acc->yorgu - y, acc->xorg - loutx, acc->xorg - linx);
2931         if (mask & 4)
2932             newFinalSpan(acc->yorgl + y, acc->xorg - loutx, acc->xorg - linx);
2933     }
2934 }
2935 
2936 static void
arcSpan0(int lx,int lw,int rx,int rw,struct arc_def * def,struct arc_bound * bounds,struct accelerators * acc,int mask)2937 arcSpan0(int lx,
2938          int lw,
2939          int rx,
2940          int rw,
2941          struct arc_def *def,
2942          struct arc_bound *bounds, struct accelerators *acc, int mask)
2943 {
2944     double x;
2945 
2946     if (boundedLe(0, bounds->inneri) &&
2947         acc->left.valid && boundedLe(0, bounds->left) && acc->left.b > 0) {
2948         x = def->w - def->l;
2949         if (acc->left.b < x)
2950             x = acc->left.b;
2951         lw = ICEIL(acc->fromIntX - x) - lx;
2952         rw += rx;
2953         rx = ICEIL(acc->fromIntX + x);
2954         rw -= rx;
2955     }
2956     arcSpan(0, lx, lw, rx, rw, def, bounds, acc, mask);
2957 }
2958 
2959 static void
tailSpan(int y,int lw,int rw,struct arc_def * def,struct arc_bound * bounds,struct accelerators * acc,int mask)2960 tailSpan(int y,
2961          int lw,
2962          int rw,
2963          struct arc_def *def,
2964          struct arc_bound *bounds, struct accelerators *acc, int mask)
2965 {
2966     double yy, xalt, x, lx, rx;
2967     int n;
2968 
2969     if (boundedLe(y, bounds->outeri))
2970         arcSpan(y, 0, lw, -rw, rw, def, bounds, acc, mask);
2971     else if (def->w != def->h) {
2972         yy = y + acc->fromIntY;
2973         x = tailX(yy, def, bounds, acc);
2974         if (yy == 0.0 && x == -rw - acc->fromIntX)
2975             return;
2976         if (acc->right.valid && boundedLe(yy, bounds->right)) {
2977             rx = x;
2978             lx = -x;
2979             xalt = intersectLine(yy, acc->right);
2980             if (xalt >= -rw - acc->fromIntX && xalt <= rx)
2981                 rx = xalt;
2982             n = ICEIL(acc->fromIntX + lx);
2983             if (lw > n) {
2984                 if (mask & 2)
2985                     newFinalSpan(acc->yorgu - y, acc->xorg + n, acc->xorg + lw);
2986                 if (mask & 4)
2987                     newFinalSpan(acc->yorgl + y, acc->xorg + n, acc->xorg + lw);
2988             }
2989             n = ICEIL(acc->fromIntX + rx);
2990             if (n > -rw) {
2991                 if (mask & 1)
2992                     newFinalSpan(acc->yorgu - y, acc->xorg - rw, acc->xorg + n);
2993                 if (mask & 8)
2994                     newFinalSpan(acc->yorgl + y, acc->xorg - rw, acc->xorg + n);
2995             }
2996         }
2997         arcSpan(y,
2998                 ICEIL(acc->fromIntX - x), 0,
2999                 ICEIL(acc->fromIntX + x), 0, def, bounds, acc, mask);
3000     }
3001 }
3002 
3003 /*
3004  * create whole arcs out of pieces.  This code is
3005  * very bad.
3006  */
3007 
3008 static struct finalSpan **finalSpans = NULL;
3009 static int finalMiny = 0, finalMaxy = -1;
3010 static int finalSize = 0;
3011 
3012 static int nspans = 0;          /* total spans, not just y coords */
3013 
3014 struct finalSpan {
3015     struct finalSpan *next;
3016     int min, max;               /* x values */
3017 };
3018 
3019 static struct finalSpan *freeFinalSpans, *tmpFinalSpan;
3020 
3021 #define allocFinalSpan()   (freeFinalSpans ?\
3022 				((tmpFinalSpan = freeFinalSpans), \
3023 				 (freeFinalSpans = freeFinalSpans->next), \
3024 				 (tmpFinalSpan->next = 0), \
3025 				 tmpFinalSpan) : \
3026 			     realAllocSpan ())
3027 
3028 #define SPAN_CHUNK_SIZE    128
3029 
3030 struct finalSpanChunk {
3031     struct finalSpan data[SPAN_CHUNK_SIZE];
3032     struct finalSpanChunk *next;
3033 };
3034 
3035 static struct finalSpanChunk *chunks;
3036 
3037 static struct finalSpan *
realAllocSpan(void)3038 realAllocSpan(void)
3039 {
3040     struct finalSpanChunk *newChunk;
3041     struct finalSpan *span;
3042     int i;
3043 
3044     newChunk = malloc(sizeof(struct finalSpanChunk));
3045     if (!newChunk)
3046         return (struct finalSpan *) NULL;
3047     newChunk->next = chunks;
3048     chunks = newChunk;
3049     freeFinalSpans = span = newChunk->data + 1;
3050     for (i = 1; i < SPAN_CHUNK_SIZE - 1; i++) {
3051         span->next = span + 1;
3052         span++;
3053     }
3054     span->next = 0;
3055     span = newChunk->data;
3056     span->next = 0;
3057     return span;
3058 }
3059 
3060 static void
disposeFinalSpans(void)3061 disposeFinalSpans(void)
3062 {
3063     struct finalSpanChunk *chunk, *next;
3064 
3065     for (chunk = chunks; chunk; chunk = next) {
3066         next = chunk->next;
3067         free(chunk);
3068     }
3069     chunks = 0;
3070     freeFinalSpans = 0;
3071     free(finalSpans);
3072     finalSpans = 0;
3073 }
3074 
3075 static void
fillSpans(DrawablePtr pDrawable,GCPtr pGC)3076 fillSpans(DrawablePtr pDrawable, GCPtr pGC)
3077 {
3078     struct finalSpan *span;
3079     DDXPointPtr xSpan;
3080     int *xWidth;
3081     int i;
3082     struct finalSpan **f;
3083     int spany;
3084     DDXPointPtr xSpans;
3085     int *xWidths;
3086 
3087     if (nspans == 0)
3088         return;
3089     xSpan = xSpans = xallocarray(nspans, sizeof(DDXPointRec));
3090     xWidth = xWidths = xallocarray(nspans, sizeof(int));
3091     if (xSpans && xWidths) {
3092         i = 0;
3093         f = finalSpans;
3094         for (spany = finalMiny; spany <= finalMaxy; spany++, f++) {
3095             for (span = *f; span; span = span->next) {
3096                 if (span->max <= span->min)
3097                     continue;
3098                 xSpan->x = span->min;
3099                 xSpan->y = spany;
3100                 ++xSpan;
3101                 *xWidth++ = span->max - span->min;
3102                 ++i;
3103             }
3104         }
3105         (*pGC->ops->FillSpans) (pDrawable, pGC, i, xSpans, xWidths, TRUE);
3106     }
3107     disposeFinalSpans();
3108     free(xSpans);
3109     free(xWidths);
3110     finalMiny = 0;
3111     finalMaxy = -1;
3112     finalSize = 0;
3113     nspans = 0;
3114 }
3115 
3116 #define SPAN_REALLOC	100
3117 
3118 #define findSpan(y) ((finalMiny <= (y) && (y) <= finalMaxy) ? \
3119 			  &finalSpans[(y) - finalMiny] : \
3120 			  realFindSpan (y))
3121 
3122 static struct finalSpan **
realFindSpan(int y)3123 realFindSpan(int y)
3124 {
3125     struct finalSpan **newSpans;
3126     int newSize, newMiny, newMaxy;
3127     int change;
3128     int i;
3129 
3130     if (y < finalMiny || y > finalMaxy) {
3131         if (!finalSize) {
3132             finalMiny = y;
3133             finalMaxy = y - 1;
3134         }
3135         if (y < finalMiny)
3136             change = finalMiny - y;
3137         else
3138             change = y - finalMaxy;
3139         if (change >= SPAN_REALLOC)
3140             change += SPAN_REALLOC;
3141         else
3142             change = SPAN_REALLOC;
3143         newSize = finalSize + change;
3144         newSpans = xallocarray(newSize, sizeof(struct finalSpan *));
3145         if (!newSpans)
3146             return NULL;
3147         newMiny = finalMiny;
3148         newMaxy = finalMaxy;
3149         if (y < finalMiny)
3150             newMiny = finalMiny - change;
3151         else
3152             newMaxy = finalMaxy + change;
3153         if (finalSpans) {
3154             memmove(((char *) newSpans) +
3155                     (finalMiny - newMiny) * sizeof(struct finalSpan *),
3156                     (char *) finalSpans,
3157                     finalSize * sizeof(struct finalSpan *));
3158             free(finalSpans);
3159         }
3160         if ((i = finalMiny - newMiny) > 0)
3161             memset((char *) newSpans, 0, i * sizeof(struct finalSpan *));
3162         if ((i = newMaxy - finalMaxy) > 0)
3163             memset((char *) (newSpans + newSize - i), 0,
3164                    i * sizeof(struct finalSpan *));
3165         finalSpans = newSpans;
3166         finalMaxy = newMaxy;
3167         finalMiny = newMiny;
3168         finalSize = newSize;
3169     }
3170     return &finalSpans[y - finalMiny];
3171 }
3172 
3173 static void
newFinalSpan(int y,int xmin,int xmax)3174 newFinalSpan(int y, int xmin, int xmax)
3175 {
3176     struct finalSpan *x;
3177     struct finalSpan **f;
3178     struct finalSpan *oldx;
3179     struct finalSpan *prev;
3180 
3181     f = findSpan(y);
3182     if (!f)
3183         return;
3184     oldx = 0;
3185     for (;;) {
3186         prev = 0;
3187         for (x = *f; x; x = x->next) {
3188             if (x == oldx) {
3189                 prev = x;
3190                 continue;
3191             }
3192             if (x->min <= xmax && xmin <= x->max) {
3193                 if (oldx) {
3194                     oldx->min = min(x->min, xmin);
3195                     oldx->max = max(x->max, xmax);
3196                     if (prev)
3197                         prev->next = x->next;
3198                     else
3199                         *f = x->next;
3200                     --nspans;
3201                 }
3202                 else {
3203                     x->min = min(x->min, xmin);
3204                     x->max = max(x->max, xmax);
3205                     oldx = x;
3206                 }
3207                 xmin = oldx->min;
3208                 xmax = oldx->max;
3209                 break;
3210             }
3211             prev = x;
3212         }
3213         if (!x)
3214             break;
3215     }
3216     if (!oldx) {
3217         x = allocFinalSpan();
3218         if (x) {
3219             x->min = xmin;
3220             x->max = xmax;
3221             x->next = *f;
3222             *f = x;
3223             ++nspans;
3224         }
3225     }
3226 }
3227 
3228 static void
mirrorSppPoint(int quadrant,SppPointPtr sppPoint)3229 mirrorSppPoint(int quadrant, SppPointPtr sppPoint)
3230 {
3231     switch (quadrant) {
3232     case 0:
3233         break;
3234     case 1:
3235         sppPoint->x = -sppPoint->x;
3236         break;
3237     case 2:
3238         sppPoint->x = -sppPoint->x;
3239         sppPoint->y = -sppPoint->y;
3240         break;
3241     case 3:
3242         sppPoint->y = -sppPoint->y;
3243         break;
3244     }
3245     /*
3246      * and translate to X coordinate system
3247      */
3248     sppPoint->y = -sppPoint->y;
3249 }
3250 
3251 /*
3252  * split an arc into pieces which are scan-converted
3253  * in the first-quadrant and mirrored into position.
3254  * This is necessary as the scan-conversion code can
3255  * only deal with arcs completely contained in the
3256  * first quadrant.
3257  */
3258 
3259 static miArcSpanData *
drawArc(xArc * tarc,int l,int a0,int a1,miArcFacePtr right,miArcFacePtr left,miArcSpanData * spdata)3260 drawArc(xArc * tarc, int l, int a0, int a1, miArcFacePtr right,
3261         miArcFacePtr left, miArcSpanData *spdata)
3262 {                               /* save end line points */
3263     struct arc_def def;
3264     struct accelerators acc;
3265     int startq, endq, curq;
3266     int rightq, leftq = 0, righta = 0, lefta = 0;
3267     miArcFacePtr passRight, passLeft;
3268     int q0 = 0, q1 = 0, mask;
3269     struct band {
3270         int a0, a1;
3271         int mask;
3272     } band[5], sweep[20];
3273     int bandno, sweepno;
3274     int i, j;
3275     int flipRight = 0, flipLeft = 0;
3276     int copyEnd = 0;
3277 
3278     if (!spdata)
3279         spdata = miComputeWideEllipse(l, tarc);
3280     if (!spdata)
3281         return NULL;
3282 
3283     if (a1 < a0)
3284         a1 += 360 * 64;
3285     startq = a0 / (90 * 64);
3286     if (a0 == a1)
3287         endq = startq;
3288     else
3289         endq = (a1 - 1) / (90 * 64);
3290     bandno = 0;
3291     curq = startq;
3292     rightq = -1;
3293     for (;;) {
3294         switch (curq) {
3295         case 0:
3296             if (a0 > 90 * 64)
3297                 q0 = 0;
3298             else
3299                 q0 = a0;
3300             if (a1 < 360 * 64)
3301                 q1 = min(a1, 90 * 64);
3302             else
3303                 q1 = 90 * 64;
3304             if (curq == startq && a0 == q0 && rightq < 0) {
3305                 righta = q0;
3306                 rightq = curq;
3307             }
3308             if (curq == endq && a1 == q1) {
3309                 lefta = q1;
3310                 leftq = curq;
3311             }
3312             break;
3313         case 1:
3314             if (a1 < 90 * 64)
3315                 q0 = 0;
3316             else
3317                 q0 = 180 * 64 - min(a1, 180 * 64);
3318             if (a0 > 180 * 64)
3319                 q1 = 90 * 64;
3320             else
3321                 q1 = 180 * 64 - max(a0, 90 * 64);
3322             if (curq == startq && 180 * 64 - a0 == q1) {
3323                 righta = q1;
3324                 rightq = curq;
3325             }
3326             if (curq == endq && 180 * 64 - a1 == q0) {
3327                 lefta = q0;
3328                 leftq = curq;
3329             }
3330             break;
3331         case 2:
3332             if (a0 > 270 * 64)
3333                 q0 = 0;
3334             else
3335                 q0 = max(a0, 180 * 64) - 180 * 64;
3336             if (a1 < 180 * 64)
3337                 q1 = 90 * 64;
3338             else
3339                 q1 = min(a1, 270 * 64) - 180 * 64;
3340             if (curq == startq && a0 - 180 * 64 == q0) {
3341                 righta = q0;
3342                 rightq = curq;
3343             }
3344             if (curq == endq && a1 - 180 * 64 == q1) {
3345                 lefta = q1;
3346                 leftq = curq;
3347             }
3348             break;
3349         case 3:
3350             if (a1 < 270 * 64)
3351                 q0 = 0;
3352             else
3353                 q0 = 360 * 64 - min(a1, 360 * 64);
3354             q1 = 360 * 64 - max(a0, 270 * 64);
3355             if (curq == startq && 360 * 64 - a0 == q1) {
3356                 righta = q1;
3357                 rightq = curq;
3358             }
3359             if (curq == endq && 360 * 64 - a1 == q0) {
3360                 lefta = q0;
3361                 leftq = curq;
3362             }
3363             break;
3364         }
3365         band[bandno].a0 = q0;
3366         band[bandno].a1 = q1;
3367         band[bandno].mask = 1 << curq;
3368         bandno++;
3369         if (curq == endq)
3370             break;
3371         curq++;
3372         if (curq == 4) {
3373             a0 = 0;
3374             a1 -= 360 * 64;
3375             curq = 0;
3376             endq -= 4;
3377         }
3378     }
3379     sweepno = 0;
3380     for (;;) {
3381         q0 = 90 * 64;
3382         mask = 0;
3383         /*
3384          * find left-most point
3385          */
3386         for (i = 0; i < bandno; i++)
3387             if (band[i].a0 <= q0) {
3388                 q0 = band[i].a0;
3389                 q1 = band[i].a1;
3390                 mask = band[i].mask;
3391             }
3392         if (!mask)
3393             break;
3394         /*
3395          * locate next point of change
3396          */
3397         for (i = 0; i < bandno; i++)
3398             if (!(mask & band[i].mask)) {
3399                 if (band[i].a0 == q0) {
3400                     if (band[i].a1 < q1)
3401                         q1 = band[i].a1;
3402                     mask |= band[i].mask;
3403                 }
3404                 else if (band[i].a0 < q1)
3405                     q1 = band[i].a0;
3406             }
3407         /*
3408          * create a new sweep
3409          */
3410         sweep[sweepno].a0 = q0;
3411         sweep[sweepno].a1 = q1;
3412         sweep[sweepno].mask = mask;
3413         sweepno++;
3414         /*
3415          * subtract the sweep from the affected bands
3416          */
3417         for (i = 0; i < bandno; i++)
3418             if (band[i].a0 == q0) {
3419                 band[i].a0 = q1;
3420                 /*
3421                  * check if this band is empty
3422                  */
3423                 if (band[i].a0 == band[i].a1)
3424                     band[i].a1 = band[i].a0 = 90 * 64 + 1;
3425             }
3426     }
3427     computeAcc(tarc, l, &def, &acc);
3428     for (j = 0; j < sweepno; j++) {
3429         mask = sweep[j].mask;
3430         passRight = passLeft = 0;
3431         if (mask & (1 << rightq)) {
3432             if (sweep[j].a0 == righta)
3433                 passRight = right;
3434             else if (sweep[j].a1 == righta) {
3435                 passLeft = right;
3436                 flipRight = 1;
3437             }
3438         }
3439         if (mask & (1 << leftq)) {
3440             if (sweep[j].a1 == lefta) {
3441                 if (passLeft)
3442                     copyEnd = 1;
3443                 passLeft = left;
3444             }
3445             else if (sweep[j].a0 == lefta) {
3446                 if (passRight)
3447                     copyEnd = 1;
3448                 passRight = left;
3449                 flipLeft = 1;
3450             }
3451         }
3452         drawQuadrant(&def, &acc, sweep[j].a0, sweep[j].a1, mask,
3453                      passRight, passLeft, spdata);
3454     }
3455     /*
3456      * when copyEnd is set, both ends of the arc were computed
3457      * at the same time; drawQuadrant only takes one end though,
3458      * so the left end will be the only one holding the data.  Copy
3459      * it from there.
3460      */
3461     if (copyEnd)
3462         *right = *left;
3463     /*
3464      * mirror the coordinates generated for the
3465      * faces of the arc
3466      */
3467     if (right) {
3468         mirrorSppPoint(rightq, &right->clock);
3469         mirrorSppPoint(rightq, &right->center);
3470         mirrorSppPoint(rightq, &right->counterClock);
3471         if (flipRight) {
3472             SppPointRec temp;
3473 
3474             temp = right->clock;
3475             right->clock = right->counterClock;
3476             right->counterClock = temp;
3477         }
3478     }
3479     if (left) {
3480         mirrorSppPoint(leftq, &left->counterClock);
3481         mirrorSppPoint(leftq, &left->center);
3482         mirrorSppPoint(leftq, &left->clock);
3483         if (flipLeft) {
3484             SppPointRec temp;
3485 
3486             temp = left->clock;
3487             left->clock = left->counterClock;
3488             left->counterClock = temp;
3489         }
3490     }
3491     return spdata;
3492 }
3493 
3494 static void
drawQuadrant(struct arc_def * def,struct accelerators * acc,int a0,int a1,int mask,miArcFacePtr right,miArcFacePtr left,miArcSpanData * spdata)3495 drawQuadrant(struct arc_def *def,
3496              struct accelerators *acc,
3497              int a0,
3498              int a1,
3499              int mask,
3500              miArcFacePtr right, miArcFacePtr left, miArcSpanData * spdata)
3501 {
3502     struct arc_bound bound;
3503     double yy, x, xalt;
3504     int y, miny, maxy;
3505     int n;
3506     miArcSpan *span;
3507 
3508     def->a0 = ((double) a0) / 64.0;
3509     def->a1 = ((double) a1) / 64.0;
3510     computeBound(def, &bound, acc, right, left);
3511     yy = bound.inner.min;
3512     if (bound.outer.min < yy)
3513         yy = bound.outer.min;
3514     miny = ICEIL(yy - acc->fromIntY);
3515     yy = bound.inner.max;
3516     if (bound.outer.max > yy)
3517         yy = bound.outer.max;
3518     maxy = floor(yy - acc->fromIntY);
3519     y = spdata->k;
3520     span = spdata->spans;
3521     if (spdata->top) {
3522         if (a1 == 90 * 64 && (mask & 1))
3523             newFinalSpan(acc->yorgu - y - 1, acc->xorg, acc->xorg + 1);
3524         span++;
3525     }
3526     for (n = spdata->count1; --n >= 0;) {
3527         if (y < miny)
3528             return;
3529         if (y <= maxy) {
3530             arcSpan(y,
3531                     span->lx, -span->lx, 0, span->lx + span->lw,
3532                     def, &bound, acc, mask);
3533             if (span->rw + span->rx)
3534                 tailSpan(y, -span->rw, -span->rx, def, &bound, acc, mask);
3535         }
3536         y--;
3537         span++;
3538     }
3539     if (y < miny)
3540         return;
3541     if (spdata->hole) {
3542         if (y <= maxy)
3543             arcSpan(y, 0, 0, 0, 1, def, &bound, acc, mask & 0xc);
3544     }
3545     for (n = spdata->count2; --n >= 0;) {
3546         if (y < miny)
3547             return;
3548         if (y <= maxy)
3549             arcSpan(y, span->lx, span->lw, span->rx, span->rw,
3550                     def, &bound, acc, mask);
3551         y--;
3552         span++;
3553     }
3554     if (spdata->bot && miny <= y && y <= maxy) {
3555         n = mask;
3556         if (y == miny)
3557             n &= 0xc;
3558         if (span->rw <= 0) {
3559             arcSpan0(span->lx, -span->lx, 0, span->lx + span->lw,
3560                      def, &bound, acc, n);
3561             if (span->rw + span->rx)
3562                 tailSpan(y, -span->rw, -span->rx, def, &bound, acc, n);
3563         }
3564         else
3565             arcSpan0(span->lx, span->lw, span->rx, span->rw,
3566                      def, &bound, acc, n);
3567         y--;
3568     }
3569     while (y >= miny) {
3570         yy = y + acc->fromIntY;
3571         if (def->w == def->h) {
3572             xalt = def->w - def->l;
3573             x = -sqrt(xalt * xalt - yy * yy);
3574         }
3575         else {
3576             x = tailX(yy, def, &bound, acc);
3577             if (acc->left.valid && boundedLe(yy, bound.left)) {
3578                 xalt = intersectLine(yy, acc->left);
3579                 if (xalt < x)
3580                     x = xalt;
3581             }
3582             if (acc->right.valid && boundedLe(yy, bound.right)) {
3583                 xalt = intersectLine(yy, acc->right);
3584                 if (xalt < x)
3585                     x = xalt;
3586             }
3587         }
3588         arcSpan(y,
3589                 ICEIL(acc->fromIntX - x), 0,
3590                 ICEIL(acc->fromIntX + x), 0, def, &bound, acc, mask);
3591         y--;
3592     }
3593 }
3594 
3595 void
miPolyArc(DrawablePtr pDraw,GCPtr pGC,int narcs,xArc * parcs)3596 miPolyArc(DrawablePtr pDraw, GCPtr pGC, int narcs, xArc * parcs)
3597 {
3598     if (pGC->lineWidth == 0)
3599         miZeroPolyArc(pDraw, pGC, narcs, parcs);
3600     else
3601         miWideArc(pDraw, pGC, narcs, parcs);
3602 }
3603