miwideline.c revision 05b261ec
1/*
2
3Copyright 1988, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included
12in all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
18OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20OTHER DEALINGS IN THE SOFTWARE.
21
22Except as contained in this notice, the name of The Open Group shall
23not be used in advertising or otherwise to promote the sale, use or
24other dealings in this Software without prior written authorization
25from The Open Group.
26
27*/
28
29/* Author:  Keith Packard, MIT X Consortium */
30
31/*
32 * Mostly integer wideline code.  Uses a technique similar to
33 * bresenham zero-width lines, except walks an X edge
34 */
35
36#ifdef HAVE_DIX_CONFIG_H
37#include <dix-config.h>
38#endif
39
40#include <stdio.h>
41#ifdef _XOPEN_SOURCE
42#include <math.h>
43#else
44#define _XOPEN_SOURCE	/* to get prototype for hypot on some systems */
45#include <math.h>
46#undef _XOPEN_SOURCE
47#endif
48#include <X11/X.h>
49#include "windowstr.h"
50#include "gcstruct.h"
51#include "regionstr.h"
52#include "miwideline.h"
53#include "mi.h"
54
55static void miLineArc(DrawablePtr pDraw, GCPtr pGC,
56		      unsigned long pixel, SpanDataPtr spanData,
57		      LineFacePtr leftFace,
58		      LineFacePtr rightFace,
59		      double xorg, double yorg, Bool isInt);
60
61
62/*
63 * spans-based polygon filler
64 */
65
66static void
67miFillPolyHelper (DrawablePtr pDrawable, GCPtr pGC, unsigned long pixel,
68		  SpanDataPtr spanData, int y, int overall_height,
69		  PolyEdgePtr left, PolyEdgePtr right,
70		  int left_count, int right_count)
71{
72    int left_x = 0, left_e = 0;
73    int	left_stepx = 0;
74    int	left_signdx = 0;
75    int	left_dy = 0, left_dx = 0;
76
77    int right_x = 0, right_e = 0;
78    int	right_stepx = 0;
79    int	right_signdx = 0;
80    int	right_dy = 0, right_dx = 0;
81
82    int	height = 0;
83    int	left_height = 0, right_height = 0;
84
85    DDXPointPtr ppt;
86    DDXPointPtr pptInit = NULL;
87    int 	*pwidth;
88    int 	*pwidthInit = NULL;
89    XID		oldPixel;
90    int		xorg;
91    Spans	spanRec;
92
93    left_height = 0;
94    right_height = 0;
95
96    if (!spanData)
97    {
98    	pptInit = (DDXPointPtr) ALLOCATE_LOCAL (overall_height * sizeof(*ppt));
99    	if (!pptInit)
100	    return;
101    	pwidthInit = (int *) ALLOCATE_LOCAL (overall_height * sizeof(*pwidth));
102    	if (!pwidthInit)
103    	{
104	    DEALLOCATE_LOCAL (pptInit);
105	    return;
106    	}
107	ppt = pptInit;
108	pwidth = pwidthInit;
109    	oldPixel = pGC->fgPixel;
110    	if (pixel != oldPixel)
111    	{
112	    XID tmpPixel = (XID)pixel;
113    	    DoChangeGC (pGC, GCForeground, &tmpPixel, FALSE);
114    	    ValidateGC (pDrawable, pGC);
115    	}
116    }
117    else
118    {
119	spanRec.points = (DDXPointPtr) xalloc (overall_height * sizeof (*ppt));
120	if (!spanRec.points)
121	    return;
122	spanRec.widths = (int *) xalloc (overall_height * sizeof (int));
123	if (!spanRec.widths)
124	{
125	    xfree (spanRec.points);
126	    return;
127	}
128	ppt = spanRec.points;
129	pwidth = spanRec.widths;
130    }
131
132    xorg = 0;
133    if (pGC->miTranslate)
134    {
135	y += pDrawable->y;
136	xorg = pDrawable->x;
137    }
138    while ((left_count || left_height) &&
139	   (right_count || right_height))
140    {
141	MIPOLYRELOADLEFT
142	MIPOLYRELOADRIGHT
143
144	height = left_height;
145	if (height > right_height)
146	    height = right_height;
147
148	left_height -= height;
149	right_height -= height;
150
151	while (--height >= 0)
152	{
153	    if (right_x >= left_x)
154	    {
155		ppt->y = y;
156		ppt->x = left_x + xorg;
157		ppt++;
158		*pwidth++ = right_x - left_x + 1;
159	    }
160    	    y++;
161
162	    MIPOLYSTEPLEFT
163
164	    MIPOLYSTEPRIGHT
165	}
166    }
167    if (!spanData)
168    {
169    	(*pGC->ops->FillSpans) (pDrawable, pGC, ppt - pptInit, pptInit, pwidthInit, TRUE);
170    	DEALLOCATE_LOCAL (pwidthInit);
171    	DEALLOCATE_LOCAL (pptInit);
172    	if (pixel != oldPixel)
173    	{
174	    DoChangeGC (pGC, GCForeground, &oldPixel, FALSE);
175	    ValidateGC (pDrawable, pGC);
176    	}
177    }
178    else
179    {
180	spanRec.count = ppt - spanRec.points;
181	AppendSpanGroup (pGC, pixel, &spanRec, spanData)
182    }
183}
184
185static void
186miFillRectPolyHelper (
187    DrawablePtr	pDrawable,
188    GCPtr	pGC,
189    unsigned long   pixel,
190    SpanDataPtr	spanData,
191    int		x,
192    int		y,
193    int		w,
194    int		h)
195{
196    DDXPointPtr ppt;
197    int 	*pwidth;
198    XID		oldPixel;
199    Spans	spanRec;
200    xRectangle  rect;
201
202    if (!spanData)
203    {
204	rect.x = x;
205	rect.y = y;
206	rect.width = w;
207	rect.height = h;
208    	oldPixel = pGC->fgPixel;
209    	if (pixel != oldPixel)
210    	{
211	    XID tmpPixel = (XID)pixel;
212    	    DoChangeGC (pGC, GCForeground, &tmpPixel, FALSE);
213    	    ValidateGC (pDrawable, pGC);
214    	}
215	(*pGC->ops->PolyFillRect) (pDrawable, pGC, 1, &rect);
216    	if (pixel != oldPixel)
217    	{
218	    DoChangeGC (pGC, GCForeground, &oldPixel, FALSE);
219	    ValidateGC (pDrawable, pGC);
220    	}
221    }
222    else
223    {
224	spanRec.points = (DDXPointPtr) xalloc (h * sizeof (*ppt));
225	if (!spanRec.points)
226	    return;
227	spanRec.widths = (int *) xalloc (h * sizeof (int));
228	if (!spanRec.widths)
229	{
230	    xfree (spanRec.points);
231	    return;
232	}
233	ppt = spanRec.points;
234	pwidth = spanRec.widths;
235
236    	if (pGC->miTranslate)
237    	{
238	    y += pDrawable->y;
239	    x += pDrawable->x;
240    	}
241	while (h--)
242	{
243	    ppt->x = x;
244	    ppt->y = y;
245	    ppt++;
246	    *pwidth++ = w;
247	    y++;
248	}
249	spanRec.count = ppt - spanRec.points;
250	AppendSpanGroup (pGC, pixel, &spanRec, spanData)
251    }
252}
253
254_X_EXPORT /* static */ int
255miPolyBuildEdge (x0, y0, k, dx, dy, xi, yi, left, edge)
256    double	x0, y0;
257    double	k;  /* x0 * dy - y0 * dx */
258    int 	dx, dy;
259    int		xi, yi;
260    int		left;
261    PolyEdgePtr edge;
262{
263    int	    x, y, e;
264    int	    xady;
265
266    if (dy < 0)
267    {
268	dy = -dy;
269	dx = -dx;
270	k = -k;
271    }
272
273#ifdef NOTDEF
274    {
275	double	realk, kerror;
276    	realk = x0 * dy - y0 * dx;
277    	kerror = Fabs (realk - k);
278    	if (kerror > .1)
279	    printf ("realk: %g k: %g\n", realk, k);
280    }
281#endif
282    y = ICEIL (y0);
283    xady = ICEIL (k) + y * dx;
284
285    if (xady <= 0)
286	x = - (-xady / dy) - 1;
287    else
288	x = (xady - 1) / dy;
289
290    e = xady - x * dy;
291
292    if (dx >= 0)
293    {
294	edge->signdx = 1;
295	edge->stepx = dx / dy;
296	edge->dx = dx % dy;
297    }
298    else
299    {
300	edge->signdx = -1;
301	edge->stepx = - (-dx / dy);
302	edge->dx = -dx % dy;
303	e = dy - e + 1;
304    }
305    edge->dy = dy;
306    edge->x = x + left + xi;
307    edge->e = e - dy;	/* bias to compare against 0 instead of dy */
308    return y + yi;
309}
310
311#define StepAround(v, incr, max) (((v) + (incr) < 0) ? (max - 1) : ((v) + (incr) == max) ? 0 : ((v) + (incr)))
312
313_X_EXPORT /* static */ int
314miPolyBuildPoly (vertices, slopes, count, xi, yi, left, right, pnleft, pnright, h)
315    PolyVertexPtr 	vertices;
316    PolySlopePtr	slopes;
317    int			count;
318    int		   	xi, yi;
319    PolyEdgePtr	    	left, right;
320    int		    	*pnleft, *pnright;
321    int		    	*h;
322{
323    int 	top, bottom;
324    double 	miny, maxy;
325    int 	i;
326    int		j;
327    int		clockwise;
328    int		slopeoff;
329    int 	s;
330    int 	nright, nleft;
331    int	   	y, lasty = 0, bottomy, topy = 0;
332
333    /* find the top of the polygon */
334    maxy = miny = vertices[0].y;
335    bottom = top = 0;
336    for (i = 1; i < count; i++)
337    {
338	if (vertices[i].y < miny)
339	{
340	    top = i;
341	    miny = vertices[i].y;
342	}
343	if (vertices[i].y >= maxy)
344	{
345	    bottom = i;
346	    maxy = vertices[i].y;
347	}
348    }
349    clockwise = 1;
350    slopeoff = 0;
351
352    i = top;
353    j = StepAround (top, -1, count);
354
355    if (slopes[j].dy * slopes[i].dx > slopes[i].dy * slopes[j].dx)
356    {
357	clockwise = -1;
358	slopeoff = -1;
359    }
360
361    bottomy = ICEIL (maxy) + yi;
362
363    nright = 0;
364
365    s = StepAround (top, slopeoff, count);
366    i = top;
367    while (i != bottom)
368    {
369	if (slopes[s].dy != 0)
370	{
371	    y = miPolyBuildEdge (vertices[i].x, vertices[i].y,
372			slopes[s].k,
373			slopes[s].dx, slopes[s].dy,
374			xi, yi, 0,
375			&right[nright]);
376	    if (nright != 0)
377	    	right[nright-1].height = y - lasty;
378	    else
379	    	topy = y;
380	    nright++;
381	    lasty = y;
382	}
383
384	i = StepAround (i, clockwise, count);
385	s = StepAround (s, clockwise, count);
386    }
387    if (nright != 0)
388	right[nright-1].height = bottomy - lasty;
389
390    if (slopeoff == 0)
391	slopeoff = -1;
392    else
393	slopeoff = 0;
394
395    nleft = 0;
396    s = StepAround (top, slopeoff, count);
397    i = top;
398    while (i != bottom)
399    {
400	if (slopes[s].dy != 0)
401	{
402	    y = miPolyBuildEdge (vertices[i].x, vertices[i].y,
403			   slopes[s].k,
404		       	   slopes[s].dx,  slopes[s].dy, xi, yi, 1,
405		       	   &left[nleft]);
406
407	    if (nleft != 0)
408	    	left[nleft-1].height = y - lasty;
409	    nleft++;
410	    lasty = y;
411	}
412	i = StepAround (i, -clockwise, count);
413	s = StepAround (s, -clockwise, count);
414    }
415    if (nleft != 0)
416	left[nleft-1].height = bottomy - lasty;
417    *pnleft = nleft;
418    *pnright = nright;
419    *h = bottomy - topy;
420    return topy;
421}
422
423static void
424miLineOnePoint (
425    DrawablePtr	    pDrawable,
426    GCPtr	    pGC,
427    unsigned long   pixel,
428    SpanDataPtr	    spanData,
429    int		    x,
430    int		    y)
431{
432    DDXPointRec pt;
433    int	    wid;
434    unsigned long	oldPixel;
435
436    MILINESETPIXEL (pDrawable, pGC, pixel, oldPixel);
437    if (pGC->fillStyle == FillSolid)
438    {
439	pt.x = x;
440	pt.y = y;
441	(*pGC->ops->PolyPoint) (pDrawable, pGC, CoordModeOrigin, 1, &pt);
442    }
443    else
444    {
445	wid = 1;
446	if (pGC->miTranslate)
447	{
448	    x += pDrawable->x;
449	    y += pDrawable->y;
450	}
451	pt.x = x;
452	pt.y = y;
453	(*pGC->ops->FillSpans) (pDrawable, pGC, 1, &pt, &wid, TRUE);
454    }
455    MILINERESETPIXEL (pDrawable, pGC, pixel, oldPixel);
456}
457
458static void
459miLineJoin (
460    DrawablePtr 	pDrawable,
461    GCPtr		pGC,
462    unsigned long	pixel,
463    SpanDataPtr		spanData,
464    LineFacePtr		pLeft,
465    LineFacePtr 	pRight)
466{
467    double	    mx = 0, my = 0;
468    double	    denom = 0.0;
469    PolyVertexRec   vertices[4];
470    PolySlopeRec    slopes[4];
471    int		    edgecount;
472    PolyEdgeRec	    left[4], right[4];
473    int		    nleft, nright;
474    int		    y, height;
475    int		    swapslopes;
476    int		    joinStyle = pGC->joinStyle;
477    int		    lw = pGC->lineWidth;
478
479    if (lw == 1 && !spanData) {
480	/* See if one of the lines will draw the joining pixel */
481	if (pLeft->dx > 0 || (pLeft->dx == 0 && pLeft->dy > 0))
482	    return;
483	if (pRight->dx > 0 || (pRight->dx == 0 && pRight->dy > 0))
484	    return;
485	if (joinStyle != JoinRound) {
486    	    denom = - pLeft->dx * (double)pRight->dy + pRight->dx * (double)pLeft->dy;
487    	    if (denom == 0)
488	    	return;	/* no join to draw */
489	}
490	if (joinStyle != JoinMiter) {
491	    miLineOnePoint (pDrawable, pGC, pixel, spanData, pLeft->x, pLeft->y);
492	    return;
493	}
494    } else {
495    	if (joinStyle == JoinRound)
496    	{
497	    miLineArc(pDrawable, pGC, pixel, spanData,
498		      pLeft, pRight,
499		      (double)0.0, (double)0.0, TRUE);
500	    return;
501    	}
502    	denom = - pLeft->dx * (double)pRight->dy + pRight->dx * (double)pLeft->dy;
503    	if (denom == 0.0)
504	    return;	/* no join to draw */
505    }
506
507    swapslopes = 0;
508    if (denom > 0)
509    {
510	pLeft->xa = -pLeft->xa;
511	pLeft->ya = -pLeft->ya;
512	pLeft->dx = -pLeft->dx;
513	pLeft->dy = -pLeft->dy;
514    }
515    else
516    {
517	swapslopes = 1;
518	pRight->xa = -pRight->xa;
519	pRight->ya = -pRight->ya;
520	pRight->dx = -pRight->dx;
521	pRight->dy = -pRight->dy;
522    }
523
524    vertices[0].x = pRight->xa;
525    vertices[0].y = pRight->ya;
526    slopes[0].dx = -pRight->dy;
527    slopes[0].dy =  pRight->dx;
528    slopes[0].k = 0;
529
530    vertices[1].x = 0;
531    vertices[1].y = 0;
532    slopes[1].dx =  pLeft->dy;
533    slopes[1].dy = -pLeft->dx;
534    slopes[1].k = 0;
535
536    vertices[2].x = pLeft->xa;
537    vertices[2].y = pLeft->ya;
538
539    if (joinStyle == JoinMiter)
540    {
541    	my = (pLeft->dy  * (pRight->xa * pRight->dy - pRight->ya * pRight->dx) -
542              pRight->dy * (pLeft->xa  * pLeft->dy  - pLeft->ya  * pLeft->dx )) /
543	      denom;
544    	if (pLeft->dy != 0)
545    	{
546	    mx = pLeft->xa + (my - pLeft->ya) *
547			    (double) pLeft->dx / (double) pLeft->dy;
548    	}
549    	else
550    	{
551	    mx = pRight->xa + (my - pRight->ya) *
552			    (double) pRight->dx / (double) pRight->dy;
553    	}
554	/* check miter limit */
555	if ((mx * mx + my * my) * 4 > SQSECANT * lw * lw)
556	    joinStyle = JoinBevel;
557    }
558
559    if (joinStyle == JoinMiter)
560    {
561	slopes[2].dx = pLeft->dx;
562	slopes[2].dy = pLeft->dy;
563	slopes[2].k =  pLeft->k;
564	if (swapslopes)
565	{
566	    slopes[2].dx = -slopes[2].dx;
567	    slopes[2].dy = -slopes[2].dy;
568	    slopes[2].k  = -slopes[2].k;
569	}
570	vertices[3].x = mx;
571	vertices[3].y = my;
572	slopes[3].dx = pRight->dx;
573	slopes[3].dy = pRight->dy;
574	slopes[3].k  = pRight->k;
575	if (swapslopes)
576	{
577	    slopes[3].dx = -slopes[3].dx;
578	    slopes[3].dy = -slopes[3].dy;
579	    slopes[3].k  = -slopes[3].k;
580	}
581	edgecount = 4;
582    }
583    else
584    {
585	double	scale, dx, dy, adx, ady;
586
587	adx = dx = pRight->xa - pLeft->xa;
588	ady = dy = pRight->ya - pLeft->ya;
589	if (adx < 0)
590	    adx = -adx;
591	if (ady < 0)
592	    ady = -ady;
593	scale = ady;
594	if (adx > ady)
595	    scale = adx;
596	slopes[2].dx = (dx * 65536) / scale;
597	slopes[2].dy = (dy * 65536) / scale;
598	slopes[2].k = ((pLeft->xa + pRight->xa) * slopes[2].dy -
599		       (pLeft->ya + pRight->ya) * slopes[2].dx) / 2.0;
600	edgecount = 3;
601    }
602
603    y = miPolyBuildPoly (vertices, slopes, edgecount, pLeft->x, pLeft->y,
604		   left, right, &nleft, &nright, &height);
605    miFillPolyHelper (pDrawable, pGC, pixel, spanData, y, height, left, right, nleft, nright);
606}
607
608static int
609miLineArcI (
610    DrawablePtr	    pDraw,
611    GCPtr	    pGC,
612    int		    xorg,
613    int		    yorg,
614    DDXPointPtr	    points,
615    int		    *widths)
616{
617    DDXPointPtr tpts, bpts;
618    int *twids, *bwids;
619    int x, y, e, ex, slw;
620
621    tpts = points;
622    twids = widths;
623    if (pGC->miTranslate)
624    {
625	xorg += pDraw->x;
626	yorg += pDraw->y;
627    }
628    slw = pGC->lineWidth;
629    if (slw == 1)
630    {
631	tpts->x = xorg;
632	tpts->y = yorg;
633	*twids = 1;
634	return 1;
635    }
636    bpts = tpts + slw;
637    bwids = twids + slw;
638    y = (slw >> 1) + 1;
639    if (slw & 1)
640	e = - ((y << 2) + 3);
641    else
642	e = - (y << 3);
643    ex = -4;
644    x = 0;
645    while (y)
646    {
647	e += (y << 3) - 4;
648	while (e >= 0)
649	{
650	    x++;
651	    e += (ex = -((x << 3) + 4));
652	}
653	y--;
654	slw = (x << 1) + 1;
655	if ((e == ex) && (slw > 1))
656	    slw--;
657	tpts->x = xorg - x;
658	tpts->y = yorg - y;
659	tpts++;
660	*twids++ = slw;
661	if ((y != 0) && ((slw > 1) || (e != ex)))
662	{
663	    bpts--;
664	    bpts->x = xorg - x;
665	    bpts->y = yorg + y;
666	    *--bwids = slw;
667	}
668    }
669    return (pGC->lineWidth);
670}
671
672#define CLIPSTEPEDGE(edgey,edge,edgeleft) \
673    if (ybase == edgey) \
674    { \
675	if (edgeleft) \
676	{ \
677	    if (edge->x > xcl) \
678		xcl = edge->x; \
679	} \
680	else \
681	{ \
682	    if (edge->x < xcr) \
683		xcr = edge->x; \
684	} \
685	edgey++; \
686	edge->x += edge->stepx; \
687	edge->e += edge->dx; \
688	if (edge->e > 0) \
689	{ \
690	    edge->x += edge->signdx; \
691	    edge->e -= edge->dy; \
692	} \
693    }
694
695static int
696miLineArcD (
697    DrawablePtr	    pDraw,
698    GCPtr	    pGC,
699    double	    xorg,
700    double	    yorg,
701    DDXPointPtr	    points,
702    int		    *widths,
703    PolyEdgePtr	    edge1,
704    int		    edgey1,
705    Bool	    edgeleft1,
706    PolyEdgePtr	    edge2,
707    int		    edgey2,
708    Bool	    edgeleft2)
709{
710    DDXPointPtr pts;
711    int *wids;
712    double radius, x0, y0, el, er, yk, xlk, xrk, k;
713    int xbase, ybase, y, boty, xl, xr, xcl, xcr;
714    int ymin, ymax;
715    Bool edge1IsMin, edge2IsMin;
716    int ymin1, ymin2;
717
718    pts = points;
719    wids = widths;
720    xbase = floor(xorg);
721    x0 = xorg - xbase;
722    ybase = ICEIL (yorg);
723    y0 = yorg - ybase;
724    if (pGC->miTranslate)
725    {
726	xbase += pDraw->x;
727	ybase += pDraw->y;
728	edge1->x += pDraw->x;
729	edge2->x += pDraw->x;
730	edgey1 += pDraw->y;
731	edgey2 += pDraw->y;
732    }
733    xlk = x0 + x0 + 1.0;
734    xrk = x0 + x0 - 1.0;
735    yk = y0 + y0 - 1.0;
736    radius = ((double)pGC->lineWidth) / 2.0;
737    y = floor(radius - y0 + 1.0);
738    ybase -= y;
739    ymin = ybase;
740    ymax = 65536;
741    edge1IsMin = FALSE;
742    ymin1 = edgey1;
743    if (edge1->dy >= 0)
744    {
745    	if (!edge1->dy)
746    	{
747	    if (edgeleft1)
748	    	edge1IsMin = TRUE;
749	    else
750	    	ymax = edgey1;
751	    edgey1 = 65536;
752    	}
753    	else
754    	{
755	    if ((edge1->signdx < 0) == edgeleft1)
756	    	edge1IsMin = TRUE;
757    	}
758    }
759    edge2IsMin = FALSE;
760    ymin2 = edgey2;
761    if (edge2->dy >= 0)
762    {
763    	if (!edge2->dy)
764    	{
765	    if (edgeleft2)
766	    	edge2IsMin = TRUE;
767	    else
768	    	ymax = edgey2;
769	    edgey2 = 65536;
770    	}
771    	else
772    	{
773	    if ((edge2->signdx < 0) == edgeleft2)
774	    	edge2IsMin = TRUE;
775    	}
776    }
777    if (edge1IsMin)
778    {
779	ymin = ymin1;
780	if (edge2IsMin && ymin1 > ymin2)
781	    ymin = ymin2;
782    } else if (edge2IsMin)
783	ymin = ymin2;
784    el = radius * radius - ((y + y0) * (y + y0)) - (x0 * x0);
785    er = el + xrk;
786    xl = 1;
787    xr = 0;
788    if (x0 < 0.5)
789    {
790	xl = 0;
791	el -= xlk;
792    }
793    boty = (y0 < -0.5) ? 1 : 0;
794    if (ybase + y - boty > ymax)
795	boty = ymax - ybase - y;
796    while (y > boty)
797    {
798	k = (y << 1) + yk;
799	er += k;
800	while (er > 0.0)
801	{
802	    xr++;
803	    er += xrk - (xr << 1);
804	}
805	el += k;
806	while (el >= 0.0)
807	{
808	    xl--;
809	    el += (xl << 1) - xlk;
810	}
811	y--;
812	ybase++;
813	if (ybase < ymin)
814	    continue;
815	xcl = xl + xbase;
816	xcr = xr + xbase;
817	CLIPSTEPEDGE(edgey1, edge1, edgeleft1);
818	CLIPSTEPEDGE(edgey2, edge2, edgeleft2);
819	if (xcr >= xcl)
820	{
821	    pts->x = xcl;
822	    pts->y = ybase;
823	    pts++;
824	    *wids++ = xcr - xcl + 1;
825	}
826    }
827    er = xrk - (xr << 1) - er;
828    el = (xl << 1) - xlk - el;
829    boty = floor(-y0 - radius + 1.0);
830    if (ybase + y - boty > ymax)
831	boty = ymax - ybase - y;
832    while (y > boty)
833    {
834	k = (y << 1) + yk;
835	er -= k;
836	while ((er >= 0.0) && (xr >= 0))
837	{
838	    xr--;
839	    er += xrk - (xr << 1);
840	}
841	el -= k;
842	while ((el > 0.0) && (xl <= 0))
843	{
844	    xl++;
845	    el += (xl << 1) - xlk;
846	}
847	y--;
848	ybase++;
849	if (ybase < ymin)
850	    continue;
851	xcl = xl + xbase;
852	xcr = xr + xbase;
853	CLIPSTEPEDGE(edgey1, edge1, edgeleft1);
854	CLIPSTEPEDGE(edgey2, edge2, edgeleft2);
855	if (xcr >= xcl)
856	{
857	    pts->x = xcl;
858	    pts->y = ybase;
859	    pts++;
860	    *wids++ = xcr - xcl + 1;
861	}
862    }
863    return (pts - points);
864}
865
866static int
867miRoundJoinFace (LineFacePtr face, PolyEdgePtr edge, Bool *leftEdge)
868{
869    int	    y;
870    int	    dx, dy;
871    double  xa, ya;
872    Bool	left;
873
874    dx = -face->dy;
875    dy = face->dx;
876    xa = face->xa;
877    ya = face->ya;
878    left = 1;
879    if (ya > 0)
880    {
881	ya = 0.0;
882	xa = 0.0;
883    }
884    if (dy < 0 || (dy == 0 && dx > 0))
885    {
886	dx = -dx;
887	dy = -dy;
888	left = !left;
889    }
890    if (dx == 0 && dy == 0)
891	dy = 1;
892    if (dy == 0)
893    {
894	y = ICEIL (face->ya) + face->y;
895	edge->x = -32767;
896	edge->stepx = 0;
897	edge->signdx = 0;
898	edge->e = -1;
899	edge->dy = 0;
900	edge->dx = 0;
901	edge->height = 0;
902    }
903    else
904    {
905	y = miPolyBuildEdge (xa, ya, 0.0, dx, dy, face->x, face->y, !left, edge);
906	edge->height = 32767;
907    }
908    *leftEdge = !left;
909    return y;
910}
911
912_X_EXPORT void
913miRoundJoinClip (pLeft, pRight, edge1, edge2, y1, y2, left1, left2)
914    LineFacePtr pLeft, pRight;
915    PolyEdgePtr	edge1, edge2;
916    int		*y1, *y2;
917    Bool	*left1, *left2;
918{
919    double	denom;
920
921    denom = - pLeft->dx * (double)pRight->dy + pRight->dx * (double)pLeft->dy;
922
923    if (denom >= 0)
924    {
925	pLeft->xa = -pLeft->xa;
926	pLeft->ya = -pLeft->ya;
927    }
928    else
929    {
930	pRight->xa = -pRight->xa;
931	pRight->ya = -pRight->ya;
932    }
933    *y1 = miRoundJoinFace (pLeft, edge1, left1);
934    *y2 = miRoundJoinFace (pRight, edge2, left2);
935}
936
937_X_EXPORT int
938miRoundCapClip (face, isInt, edge, leftEdge)
939    LineFacePtr face;
940    Bool	isInt;
941    PolyEdgePtr edge;
942    Bool	*leftEdge;
943{
944    int		y;
945    int 	dx, dy;
946    double	xa, ya, k;
947    Bool	left;
948
949    dx = -face->dy;
950    dy = face->dx;
951    xa = face->xa;
952    ya = face->ya;
953    k = 0.0;
954    if (!isInt)
955	k = face->k;
956    left = 1;
957    if (dy < 0 || (dy == 0 && dx > 0))
958    {
959	dx = -dx;
960	dy = -dy;
961	xa = -xa;
962	ya = -ya;
963	left = !left;
964    }
965    if (dx == 0 && dy == 0)
966	dy = 1;
967    if (dy == 0)
968    {
969	y = ICEIL (face->ya) + face->y;
970	edge->x = -32767;
971	edge->stepx = 0;
972	edge->signdx = 0;
973	edge->e = -1;
974	edge->dy = 0;
975	edge->dx = 0;
976	edge->height = 0;
977    }
978    else
979    {
980	y = miPolyBuildEdge (xa, ya, k, dx, dy, face->x, face->y, !left, edge);
981	edge->height = 32767;
982    }
983    *leftEdge = !left;
984    return y;
985}
986
987static void
988miLineArc (
989    DrawablePtr		pDraw,
990    GCPtr  		pGC,
991    unsigned long	pixel,
992    SpanDataPtr		spanData,
993    LineFacePtr		leftFace,
994    LineFacePtr 	rightFace,
995    double	    	xorg,
996    double          	yorg,
997    Bool	    	isInt)
998{
999    DDXPointPtr points;
1000    int *widths;
1001    int xorgi = 0, yorgi = 0;
1002    XID		oldPixel;
1003    Spans spanRec;
1004    int n;
1005    PolyEdgeRec	edge1, edge2;
1006    int		edgey1, edgey2;
1007    Bool	edgeleft1, edgeleft2;
1008
1009    if (isInt)
1010    {
1011	xorgi = leftFace ? leftFace->x : rightFace->x;
1012	yorgi = leftFace ? leftFace->y : rightFace->y;
1013    }
1014    edgey1 = 65536;
1015    edgey2 = 65536;
1016    edge1.x = 0; /* not used, keep memory checkers happy */
1017    edge1.dy = -1;
1018    edge2.x = 0; /* not used, keep memory checkers happy */
1019    edge2.dy = -1;
1020    edgeleft1 = FALSE;
1021    edgeleft2 = FALSE;
1022    if ((pGC->lineStyle != LineSolid || pGC->lineWidth > 2) &&
1023	((pGC->capStyle == CapRound && pGC->joinStyle != JoinRound) ||
1024	 (pGC->joinStyle == JoinRound && pGC->capStyle == CapButt)))
1025    {
1026	if (isInt)
1027	{
1028	    xorg = (double) xorgi;
1029	    yorg = (double) yorgi;
1030	}
1031	if (leftFace && rightFace)
1032	{
1033	    miRoundJoinClip (leftFace, rightFace, &edge1, &edge2,
1034			     &edgey1, &edgey2, &edgeleft1, &edgeleft2);
1035	}
1036	else if (leftFace)
1037	{
1038	    edgey1 = miRoundCapClip (leftFace, isInt, &edge1, &edgeleft1);
1039	}
1040	else if (rightFace)
1041	{
1042	    edgey2 = miRoundCapClip (rightFace, isInt, &edge2, &edgeleft2);
1043	}
1044	isInt = FALSE;
1045    }
1046    if (!spanData)
1047    {
1048    	points = (DDXPointPtr)ALLOCATE_LOCAL(sizeof(DDXPointRec) * pGC->lineWidth);
1049    	if (!points)
1050	    return;
1051    	widths = (int *)ALLOCATE_LOCAL(sizeof(int) * pGC->lineWidth);
1052    	if (!widths)
1053    	{
1054	    DEALLOCATE_LOCAL(points);
1055	    return;
1056    	}
1057    	oldPixel = pGC->fgPixel;
1058    	if (pixel != oldPixel)
1059    	{
1060	    XID tmpPixel = (XID)pixel;
1061	    DoChangeGC(pGC, GCForeground, &tmpPixel, FALSE);
1062	    ValidateGC (pDraw, pGC);
1063    	}
1064    }
1065    else
1066    {
1067	points = (DDXPointPtr) xalloc (pGC->lineWidth * sizeof (DDXPointRec));
1068	if (!points)
1069	    return;
1070	widths = (int *) xalloc (pGC->lineWidth * sizeof (int));
1071	if (!widths)
1072	{
1073	    xfree (points);
1074	    return;
1075	}
1076	spanRec.points = points;
1077	spanRec.widths = widths;
1078    }
1079    if (isInt)
1080	n = miLineArcI(pDraw, pGC, xorgi, yorgi, points, widths);
1081    else
1082	n = miLineArcD(pDraw, pGC, xorg, yorg, points, widths,
1083		       &edge1, edgey1, edgeleft1,
1084		       &edge2, edgey2, edgeleft2);
1085
1086    if (!spanData)
1087    {
1088    	(*pGC->ops->FillSpans)(pDraw, pGC, n, points, widths, TRUE);
1089    	DEALLOCATE_LOCAL(widths);
1090    	DEALLOCATE_LOCAL(points);
1091    	if (pixel != oldPixel)
1092    	{
1093	    DoChangeGC(pGC, GCForeground, &oldPixel, FALSE);
1094	    ValidateGC (pDraw, pGC);
1095    	}
1096    }
1097    else
1098    {
1099	spanRec.count = n;
1100	AppendSpanGroup (pGC, pixel, &spanRec, spanData)
1101    }
1102}
1103
1104static void
1105miLineProjectingCap (DrawablePtr pDrawable, GCPtr pGC, unsigned long pixel,
1106		     SpanDataPtr spanData, LineFacePtr face, Bool isLeft,
1107		     double xorg, double yorg, Bool isInt)
1108{
1109    int	xorgi = 0, yorgi = 0;
1110    int	lw;
1111    PolyEdgeRec	lefts[2], rights[2];
1112    int		lefty, righty, topy, bottomy;
1113    PolyEdgePtr left, right;
1114    PolyEdgePtr	top, bottom;
1115    double	xa,ya;
1116    double	k;
1117    double	xap, yap;
1118    int		dx, dy;
1119    double	projectXoff, projectYoff;
1120    double	maxy;
1121    int		finaly;
1122
1123    if (isInt)
1124    {
1125	xorgi = face->x;
1126	yorgi = face->y;
1127    }
1128    lw = pGC->lineWidth;
1129    dx = face->dx;
1130    dy = face->dy;
1131    k = face->k;
1132    if (dy == 0)
1133    {
1134	lefts[0].height = lw;
1135	lefts[0].x = xorgi;
1136	if (isLeft)
1137	    lefts[0].x -= (lw >> 1);
1138	lefts[0].stepx = 0;
1139	lefts[0].signdx = 1;
1140	lefts[0].e = -lw;
1141	lefts[0].dx = 0;
1142	lefts[0].dy = lw;
1143	rights[0].height = lw;
1144	rights[0].x = xorgi;
1145	if (!isLeft)
1146	    rights[0].x += ((lw + 1) >> 1);
1147	rights[0].stepx = 0;
1148	rights[0].signdx = 1;
1149	rights[0].e = -lw;
1150	rights[0].dx = 0;
1151	rights[0].dy = lw;
1152	miFillPolyHelper (pDrawable, pGC, pixel, spanData, yorgi - (lw >> 1), lw,
1153		     lefts, rights, 1, 1);
1154    }
1155    else if (dx == 0)
1156    {
1157	if (dy < 0) {
1158	    dy = -dy;
1159	    isLeft = !isLeft;
1160	}
1161	topy = yorgi;
1162	bottomy = yorgi + dy;
1163	if (isLeft)
1164	    topy -= (lw >> 1);
1165	else
1166	    bottomy += (lw >> 1);
1167	lefts[0].height = bottomy - topy;
1168	lefts[0].x = xorgi - (lw >> 1);
1169	lefts[0].stepx = 0;
1170	lefts[0].signdx = 1;
1171	lefts[0].e = -dy;
1172	lefts[0].dx = dx;
1173	lefts[0].dy = dy;
1174
1175	rights[0].height = bottomy - topy;
1176	rights[0].x = lefts[0].x + (lw-1);
1177	rights[0].stepx = 0;
1178	rights[0].signdx = 1;
1179	rights[0].e = -dy;
1180	rights[0].dx = dx;
1181	rights[0].dy = dy;
1182	miFillPolyHelper (pDrawable, pGC, pixel, spanData, topy, bottomy - topy, lefts, rights, 1, 1);
1183    }
1184    else
1185    {
1186	xa = face->xa;
1187	ya = face->ya;
1188	projectXoff = -ya;
1189	projectYoff = xa;
1190	if (dx < 0)
1191	{
1192	    right = &rights[1];
1193	    left = &lefts[0];
1194	    top = &rights[0];
1195	    bottom = &lefts[1];
1196	}
1197	else
1198	{
1199	    right = &rights[0];
1200	    left = &lefts[1];
1201	    top = &lefts[0];
1202	    bottom = &rights[1];
1203	}
1204	if (isLeft)
1205	{
1206	    righty = miPolyBuildEdge (xa, ya,
1207		     k, dx, dy, xorgi, yorgi, 0, right);
1208
1209	    xa = -xa;
1210	    ya = -ya;
1211	    k = -k;
1212	    lefty = miPolyBuildEdge (xa - projectXoff, ya - projectYoff,
1213				     k, dx, dy, xorgi, yorgi, 1, left);
1214	    if (dx > 0)
1215	    {
1216		ya = -ya;
1217		xa = -xa;
1218	    }
1219	    xap = xa - projectXoff;
1220	    yap = ya - projectYoff;
1221	    topy = miPolyBuildEdge (xap, yap, xap * dx + yap * dy,
1222				    -dy, dx, xorgi, yorgi, dx > 0, top);
1223	    bottomy = miPolyBuildEdge (xa, ya,
1224				       0.0, -dy, dx, xorgi, yorgi, dx < 0, bottom);
1225	    maxy = -ya;
1226	}
1227	else
1228	{
1229	    righty = miPolyBuildEdge (xa - projectXoff, ya - projectYoff,
1230		     k, dx, dy, xorgi, yorgi, 0, right);
1231
1232	    xa = -xa;
1233	    ya = -ya;
1234	    k = -k;
1235	    lefty = miPolyBuildEdge (xa, ya,
1236		    k, dx, dy, xorgi, yorgi, 1, left);
1237	    if (dx > 0)
1238	    {
1239		ya = -ya;
1240		xa = -xa;
1241	    }
1242	    xap = xa - projectXoff;
1243	    yap = ya - projectYoff;
1244	    topy = miPolyBuildEdge (xa, ya, 0.0, -dy, dx, xorgi, xorgi, dx > 0, top);
1245	    bottomy = miPolyBuildEdge (xap, yap, xap * dx + yap * dy,
1246				       -dy, dx, xorgi, xorgi, dx < 0, bottom);
1247	    maxy = -ya + projectYoff;
1248	}
1249	finaly = ICEIL(maxy) + yorgi;
1250	if (dx < 0)
1251	{
1252	    left->height = bottomy - lefty;
1253	    right->height = finaly - righty;
1254	    top->height = righty - topy;
1255	}
1256	else
1257	{
1258	    right->height =  bottomy - righty;
1259	    left->height = finaly - lefty;
1260	    top->height = lefty - topy;
1261	}
1262	bottom->height = finaly - bottomy;
1263	miFillPolyHelper (pDrawable, pGC, pixel, spanData, topy,
1264		     bottom->height + bottomy - topy, lefts, rights, 2, 2);
1265    }
1266}
1267
1268static void
1269miWideSegment (
1270    DrawablePtr		pDrawable,
1271    GCPtr		pGC,
1272    unsigned long	pixel,
1273    SpanDataPtr		spanData,
1274    int    		x1,
1275    int    		y1,
1276    int    		x2,
1277    int    		y2,
1278    Bool		projectLeft,
1279    Bool		projectRight,
1280    LineFacePtr 	leftFace,
1281    LineFacePtr 	rightFace)
1282{
1283    double	l, L, r;
1284    double	xa, ya;
1285    double	projectXoff = 0.0, projectYoff = 0.0;
1286    double	k;
1287    double	maxy;
1288    int		x, y;
1289    int		dx, dy;
1290    int		finaly;
1291    PolyEdgePtr left, right;
1292    PolyEdgePtr	top, bottom;
1293    int		lefty, righty, topy, bottomy;
1294    int		signdx;
1295    PolyEdgeRec	lefts[2], rights[2];
1296    LineFacePtr	tface;
1297    int		lw = pGC->lineWidth;
1298
1299    /* draw top-to-bottom always */
1300    if (y2 < y1 || (y2 == y1 && x2 < x1))
1301    {
1302	x = x1;
1303	x1 = x2;
1304	x2 = x;
1305
1306	y = y1;
1307	y1 = y2;
1308	y2 = y;
1309
1310	x = projectLeft;
1311	projectLeft = projectRight;
1312	projectRight = x;
1313
1314	tface = leftFace;
1315	leftFace = rightFace;
1316	rightFace = tface;
1317    }
1318
1319    dy = y2 - y1;
1320    signdx = 1;
1321    dx = x2 - x1;
1322    if (dx < 0)
1323	signdx = -1;
1324
1325    leftFace->x = x1;
1326    leftFace->y = y1;
1327    leftFace->dx = dx;
1328    leftFace->dy = dy;
1329
1330    rightFace->x = x2;
1331    rightFace->y = y2;
1332    rightFace->dx = -dx;
1333    rightFace->dy = -dy;
1334
1335    if (dy == 0)
1336    {
1337	rightFace->xa = 0;
1338	rightFace->ya = (double) lw / 2.0;
1339	rightFace->k = -(double) (lw * dx) / 2.0;
1340	leftFace->xa = 0;
1341	leftFace->ya = -rightFace->ya;
1342	leftFace->k = rightFace->k;
1343	x = x1;
1344	if (projectLeft)
1345	    x -= (lw >> 1);
1346	y = y1 - (lw >> 1);
1347	dx = x2 - x;
1348	if (projectRight)
1349	    dx += ((lw + 1) >> 1);
1350	dy = lw;
1351	miFillRectPolyHelper (pDrawable, pGC, pixel, spanData,
1352			      x, y, dx, dy);
1353    }
1354    else if (dx == 0)
1355    {
1356	leftFace->xa =  (double) lw / 2.0;
1357	leftFace->ya = 0;
1358	leftFace->k = (double) (lw * dy) / 2.0;
1359	rightFace->xa = -leftFace->xa;
1360	rightFace->ya = 0;
1361	rightFace->k = leftFace->k;
1362	y = y1;
1363	if (projectLeft)
1364	    y -= lw >> 1;
1365	x = x1 - (lw >> 1);
1366	dy = y2 - y;
1367	if (projectRight)
1368	    dy += ((lw + 1) >> 1);
1369	dx = lw;
1370	miFillRectPolyHelper (pDrawable, pGC, pixel, spanData,
1371			      x, y, dx, dy);
1372    }
1373    else
1374    {
1375    	l = ((double) lw) / 2.0;
1376    	L = hypot ((double) dx, (double) dy);
1377
1378	if (dx < 0)
1379	{
1380	    right = &rights[1];
1381	    left = &lefts[0];
1382	    top = &rights[0];
1383	    bottom = &lefts[1];
1384	}
1385	else
1386	{
1387	    right = &rights[0];
1388	    left = &lefts[1];
1389	    top = &lefts[0];
1390	    bottom = &rights[1];
1391	}
1392	r = l / L;
1393
1394	/* coord of upper bound at integral y */
1395	ya = -r * dx;
1396	xa = r * dy;
1397
1398	if (projectLeft | projectRight)
1399	{
1400	    projectXoff = -ya;
1401	    projectYoff = xa;
1402	}
1403
1404    	/* xa * dy - ya * dx */
1405	k = l * L;
1406
1407	leftFace->xa = xa;
1408	leftFace->ya = ya;
1409	leftFace->k = k;
1410	rightFace->xa = -xa;
1411	rightFace->ya = -ya;
1412	rightFace->k = k;
1413
1414	if (projectLeft)
1415	    righty = miPolyBuildEdge (xa - projectXoff, ya - projectYoff,
1416				      k, dx, dy, x1, y1, 0, right);
1417	else
1418	    righty = miPolyBuildEdge (xa, ya,
1419				      k, dx, dy, x1, y1, 0, right);
1420
1421	/* coord of lower bound at integral y */
1422	ya = -ya;
1423	xa = -xa;
1424
1425	/* xa * dy - ya * dx */
1426	k = - k;
1427
1428	if (projectLeft)
1429	    lefty = miPolyBuildEdge (xa - projectXoff, ya - projectYoff,
1430				     k, dx, dy, x1, y1, 1, left);
1431	else
1432	    lefty = miPolyBuildEdge (xa, ya,
1433				     k, dx, dy, x1, y1, 1, left);
1434
1435	/* coord of top face at integral y */
1436
1437	if (signdx > 0)
1438	{
1439	    ya = -ya;
1440	    xa = -xa;
1441	}
1442
1443	if (projectLeft)
1444	{
1445	    double xap = xa - projectXoff;
1446	    double yap = ya - projectYoff;
1447	    topy = miPolyBuildEdge (xap, yap, xap * dx + yap * dy,
1448				    -dy, dx, x1, y1, dx > 0, top);
1449	}
1450	else
1451	    topy = miPolyBuildEdge (xa, ya, 0.0, -dy, dx, x1, y1, dx > 0, top);
1452
1453	/* coord of bottom face at integral y */
1454
1455	if (projectRight)
1456	{
1457	    double xap = xa + projectXoff;
1458	    double yap = ya + projectYoff;
1459	    bottomy = miPolyBuildEdge (xap, yap, xap * dx + yap * dy,
1460				       -dy, dx, x2, y2, dx < 0, bottom);
1461	    maxy = -ya + projectYoff;
1462	}
1463	else
1464	{
1465	    bottomy = miPolyBuildEdge (xa, ya,
1466				       0.0, -dy, dx, x2, y2, dx < 0, bottom);
1467	    maxy = -ya;
1468	}
1469
1470	finaly = ICEIL (maxy) + y2;
1471
1472	if (dx < 0)
1473	{
1474	    left->height = bottomy - lefty;
1475	    right->height = finaly - righty;
1476	    top->height = righty - topy;
1477	}
1478	else
1479	{
1480	    right->height =  bottomy - righty;
1481	    left->height = finaly - lefty;
1482	    top->height = lefty - topy;
1483	}
1484	bottom->height = finaly - bottomy;
1485	miFillPolyHelper (pDrawable, pGC, pixel, spanData, topy,
1486		     bottom->height + bottomy - topy, lefts, rights, 2, 2);
1487    }
1488}
1489
1490static SpanDataPtr
1491miSetupSpanData (GCPtr pGC, SpanDataPtr spanData, int npt)
1492{
1493    if ((npt < 3 && pGC->capStyle != CapRound) || miSpansEasyRop(pGC->alu))
1494	return (SpanDataPtr) NULL;
1495    if (pGC->lineStyle == LineDoubleDash)
1496	miInitSpanGroup (&spanData->bgGroup);
1497    miInitSpanGroup (&spanData->fgGroup);
1498    return spanData;
1499}
1500
1501static void
1502miCleanupSpanData (DrawablePtr pDrawable, GCPtr pGC, SpanDataPtr spanData)
1503{
1504    if (pGC->lineStyle == LineDoubleDash)
1505    {
1506	XID oldPixel, pixel;
1507
1508	pixel = pGC->bgPixel;
1509	oldPixel = pGC->fgPixel;
1510    	if (pixel != oldPixel)
1511    	{
1512    	    DoChangeGC (pGC, GCForeground, &pixel, FALSE);
1513    	    ValidateGC (pDrawable, pGC);
1514    	}
1515	miFillUniqueSpanGroup (pDrawable, pGC, &spanData->bgGroup);
1516	miFreeSpanGroup (&spanData->bgGroup);
1517    	if (pixel != oldPixel)
1518    	{
1519	    DoChangeGC (pGC, GCForeground, &oldPixel, FALSE);
1520	    ValidateGC (pDrawable, pGC);
1521    	}
1522    }
1523    miFillUniqueSpanGroup (pDrawable, pGC, &spanData->fgGroup);
1524    miFreeSpanGroup (&spanData->fgGroup);
1525}
1526
1527_X_EXPORT void
1528miWideLine (pDrawable, pGC, mode, npt, pPts)
1529    DrawablePtr	pDrawable;
1530    GCPtr 	pGC;
1531    int		mode;
1532    int 	npt;
1533    DDXPointPtr pPts;
1534{
1535    int		x1, y1, x2, y2;
1536    SpanDataRec	spanDataRec;
1537    SpanDataPtr	spanData;
1538    long   	pixel;
1539    Bool	projectLeft, projectRight;
1540    LineFaceRec	leftFace, rightFace, prevRightFace;
1541    LineFaceRec	firstFace;
1542    int		first;
1543    Bool	somethingDrawn = FALSE;
1544    Bool	selfJoin;
1545
1546    spanData = miSetupSpanData (pGC, &spanDataRec, npt);
1547    pixel = pGC->fgPixel;
1548    x2 = pPts->x;
1549    y2 = pPts->y;
1550    first = TRUE;
1551    selfJoin = FALSE;
1552    if (npt > 1)
1553    {
1554    	if (mode == CoordModePrevious)
1555    	{
1556	    int nptTmp;
1557	    DDXPointPtr pPtsTmp;
1558
1559	    x1 = x2;
1560	    y1 = y2;
1561	    nptTmp = npt;
1562	    pPtsTmp = pPts + 1;
1563	    while (--nptTmp)
1564	    {
1565	    	x1 += pPtsTmp->x;
1566	    	y1 += pPtsTmp->y;
1567	    	++pPtsTmp;
1568	    }
1569	    if (x2 == x1 && y2 == y1)
1570	    	selfJoin = TRUE;
1571    	}
1572    	else if (x2 == pPts[npt-1].x && y2 == pPts[npt-1].y)
1573    	{
1574	    selfJoin = TRUE;
1575    	}
1576    }
1577    projectLeft = pGC->capStyle == CapProjecting && !selfJoin;
1578    projectRight = FALSE;
1579    while (--npt)
1580    {
1581	x1 = x2;
1582	y1 = y2;
1583	++pPts;
1584	x2 = pPts->x;
1585	y2 = pPts->y;
1586	if (mode == CoordModePrevious)
1587	{
1588	    x2 += x1;
1589	    y2 += y1;
1590	}
1591	if (x1 != x2 || y1 != y2)
1592	{
1593	    somethingDrawn = TRUE;
1594	    if (npt == 1 && pGC->capStyle == CapProjecting && !selfJoin)
1595	    	projectRight = TRUE;
1596	    miWideSegment (pDrawable, pGC, pixel, spanData, x1, y1, x2, y2,
1597		       	   projectLeft, projectRight, &leftFace, &rightFace);
1598	    if (first)
1599	    {
1600	    	if (selfJoin)
1601		    firstFace = leftFace;
1602	    	else if (pGC->capStyle == CapRound)
1603		{
1604		    if (pGC->lineWidth == 1 && !spanData)
1605			miLineOnePoint (pDrawable, pGC, pixel, spanData, x1, y1);
1606		    else
1607		    	miLineArc (pDrawable, pGC, pixel, spanData,
1608			       	   &leftFace, (LineFacePtr) NULL,
1609 			       	   (double)0.0, (double)0.0,
1610			       	   TRUE);
1611		}
1612	    }
1613	    else
1614	    {
1615	    	miLineJoin (pDrawable, pGC, pixel, spanData, &leftFace,
1616		            &prevRightFace);
1617	    }
1618	    prevRightFace = rightFace;
1619	    first = FALSE;
1620	    projectLeft = FALSE;
1621	}
1622	if (npt == 1 && somethingDrawn)
1623 	{
1624	    if (selfJoin)
1625		miLineJoin (pDrawable, pGC, pixel, spanData, &firstFace,
1626			    &rightFace);
1627	    else if (pGC->capStyle == CapRound)
1628	    {
1629		if (pGC->lineWidth == 1 && !spanData)
1630		    miLineOnePoint (pDrawable, pGC, pixel, spanData, x2, y2);
1631		else
1632		    miLineArc (pDrawable, pGC, pixel, spanData,
1633			       (LineFacePtr) NULL, &rightFace,
1634			       (double)0.0, (double)0.0,
1635			       TRUE);
1636	    }
1637	}
1638    }
1639    /* handle crock where all points are coincedent */
1640    if (!somethingDrawn)
1641    {
1642	projectLeft = pGC->capStyle == CapProjecting;
1643	miWideSegment (pDrawable, pGC, pixel, spanData,
1644		       x2, y2, x2, y2, projectLeft, projectLeft,
1645		       &leftFace, &rightFace);
1646	if (pGC->capStyle == CapRound)
1647	{
1648	    miLineArc (pDrawable, pGC, pixel, spanData,
1649		       &leftFace, (LineFacePtr) NULL,
1650		       (double)0.0, (double)0.0,
1651		       TRUE);
1652	    rightFace.dx = -1;	/* sleezy hack to make it work */
1653	    miLineArc (pDrawable, pGC, pixel, spanData,
1654		       (LineFacePtr) NULL, &rightFace,
1655 		       (double)0.0, (double)0.0,
1656		       TRUE);
1657	}
1658    }
1659    if (spanData)
1660	miCleanupSpanData (pDrawable, pGC, spanData);
1661}
1662
1663#define V_TOP	    0
1664#define V_RIGHT	    1
1665#define V_BOTTOM    2
1666#define V_LEFT	    3
1667
1668static void
1669miWideDashSegment (
1670    DrawablePtr	    pDrawable,
1671    GCPtr  	    pGC,
1672    SpanDataPtr	    spanData,
1673    int		    *pDashOffset,
1674    int		    *pDashIndex,
1675    int		    x1,
1676    int		    y1,
1677    int		    x2,
1678    int		    y2,
1679    Bool	    projectLeft,
1680    Bool	    projectRight,
1681    LineFacePtr	    leftFace,
1682    LineFacePtr	    rightFace)
1683{
1684    int		    dashIndex, dashRemain;
1685    unsigned char   *pDash;
1686    double	    L, l;
1687    double	    k;
1688    PolyVertexRec   vertices[4];
1689    PolyVertexRec   saveRight, saveBottom;
1690    PolySlopeRec    slopes[4];
1691    PolyEdgeRec	    left[2], right[2];
1692    LineFaceRec	    lcapFace, rcapFace;
1693    int		    nleft, nright;
1694    int		    h;
1695    int		    y;
1696    int		    dy, dx;
1697    unsigned long   pixel;
1698    double	    LRemain;
1699    double	    r;
1700    double	    rdx, rdy;
1701    double	    dashDx, dashDy;
1702    double	    saveK = 0.0;
1703    Bool	    first = TRUE;
1704    double	    lcenterx, lcentery, rcenterx = 0.0, rcentery = 0.0;
1705    unsigned long   fgPixel, bgPixel;
1706
1707    dx = x2 - x1;
1708    dy = y2 - y1;
1709    dashIndex = *pDashIndex;
1710    pDash = pGC->dash;
1711    dashRemain = pDash[dashIndex] - *pDashOffset;
1712    fgPixel = pGC->fgPixel;
1713    bgPixel = pGC->bgPixel;
1714    if (pGC->fillStyle == FillOpaqueStippled ||
1715	pGC->fillStyle == FillTiled)
1716    {
1717	bgPixel = fgPixel;
1718    }
1719
1720    l = ((double) pGC->lineWidth) / 2.0;
1721    if (dx == 0)
1722    {
1723	L = dy;
1724	rdx = 0;
1725	rdy = l;
1726	if (dy < 0)
1727	{
1728	    L = -dy;
1729	    rdy = -l;
1730	}
1731    }
1732    else if (dy == 0)
1733    {
1734	L = dx;
1735	rdx = l;
1736	rdy = 0;
1737	if (dx < 0)
1738	{
1739	    L = -dx;
1740	    rdx = -l;
1741	}
1742    }
1743    else
1744    {
1745	L = hypot ((double) dx, (double) dy);
1746	r = l / L;
1747
1748	rdx = r * dx;
1749	rdy = r * dy;
1750    }
1751    k = l * L;
1752    LRemain = L;
1753    /* All position comments are relative to a line with dx and dy > 0,
1754     * but the code does not depend on this */
1755    /* top */
1756    slopes[V_TOP].dx = dx;
1757    slopes[V_TOP].dy = dy;
1758    slopes[V_TOP].k = k;
1759    /* right */
1760    slopes[V_RIGHT].dx = -dy;
1761    slopes[V_RIGHT].dy = dx;
1762    slopes[V_RIGHT].k = 0;
1763    /* bottom */
1764    slopes[V_BOTTOM].dx = -dx;
1765    slopes[V_BOTTOM].dy = -dy;
1766    slopes[V_BOTTOM].k = k;
1767    /* left */
1768    slopes[V_LEFT].dx = dy;
1769    slopes[V_LEFT].dy = -dx;
1770    slopes[V_LEFT].k = 0;
1771
1772    /* preload the start coordinates */
1773    vertices[V_RIGHT].x = vertices[V_TOP].x = rdy;
1774    vertices[V_RIGHT].y = vertices[V_TOP].y = -rdx;
1775
1776    vertices[V_BOTTOM].x = vertices[V_LEFT].x = -rdy;
1777    vertices[V_BOTTOM].y = vertices[V_LEFT].y = rdx;
1778
1779    if (projectLeft)
1780    {
1781	vertices[V_TOP].x -= rdx;
1782	vertices[V_TOP].y -= rdy;
1783
1784	vertices[V_LEFT].x -= rdx;
1785	vertices[V_LEFT].y -= rdy;
1786
1787	slopes[V_LEFT].k = rdx * dx + rdy * dy;
1788    }
1789
1790    lcenterx = x1;
1791    lcentery = y1;
1792
1793    if (pGC->capStyle == CapRound)
1794    {
1795	lcapFace.dx = dx;
1796	lcapFace.dy = dy;
1797	lcapFace.x = x1;
1798	lcapFace.y = y1;
1799
1800	rcapFace.dx = -dx;
1801	rcapFace.dy = -dy;
1802	rcapFace.x = x1;
1803	rcapFace.y = y1;
1804    }
1805    while (LRemain > dashRemain)
1806    {
1807	dashDx = (dashRemain * dx) / L;
1808	dashDy = (dashRemain * dy) / L;
1809
1810	rcenterx = lcenterx + dashDx;
1811	rcentery = lcentery + dashDy;
1812
1813	vertices[V_RIGHT].x += dashDx;
1814	vertices[V_RIGHT].y += dashDy;
1815
1816	vertices[V_BOTTOM].x += dashDx;
1817	vertices[V_BOTTOM].y += dashDy;
1818
1819	slopes[V_RIGHT].k = vertices[V_RIGHT].x * dx + vertices[V_RIGHT].y * dy;
1820
1821	if (pGC->lineStyle == LineDoubleDash || !(dashIndex & 1))
1822	{
1823	    if (pGC->lineStyle == LineOnOffDash &&
1824 	        pGC->capStyle == CapProjecting)
1825	    {
1826		saveRight = vertices[V_RIGHT];
1827		saveBottom = vertices[V_BOTTOM];
1828		saveK = slopes[V_RIGHT].k;
1829
1830		if (!first)
1831		{
1832		    vertices[V_TOP].x -= rdx;
1833		    vertices[V_TOP].y -= rdy;
1834
1835		    vertices[V_LEFT].x -= rdx;
1836		    vertices[V_LEFT].y -= rdy;
1837
1838		    slopes[V_LEFT].k = vertices[V_LEFT].x *
1839				       slopes[V_LEFT].dy -
1840				       vertices[V_LEFT].y *
1841				       slopes[V_LEFT].dx;
1842		}
1843
1844		vertices[V_RIGHT].x += rdx;
1845		vertices[V_RIGHT].y += rdy;
1846
1847		vertices[V_BOTTOM].x += rdx;
1848		vertices[V_BOTTOM].y += rdy;
1849
1850		slopes[V_RIGHT].k = vertices[V_RIGHT].x *
1851				   slopes[V_RIGHT].dy -
1852				   vertices[V_RIGHT].y *
1853				   slopes[V_RIGHT].dx;
1854	    }
1855	    y = miPolyBuildPoly (vertices, slopes, 4, x1, y1,
1856			     	 left, right, &nleft, &nright, &h);
1857	    pixel = (dashIndex & 1) ? bgPixel : fgPixel;
1858	    miFillPolyHelper (pDrawable, pGC, pixel, spanData, y, h, left, right, nleft, nright);
1859
1860	    if (pGC->lineStyle == LineOnOffDash)
1861	    {
1862		switch (pGC->capStyle)
1863		{
1864		case CapProjecting:
1865		    vertices[V_BOTTOM] = saveBottom;
1866		    vertices[V_RIGHT] = saveRight;
1867		    slopes[V_RIGHT].k = saveK;
1868		    break;
1869		case CapRound:
1870		    if (!first)
1871		    {
1872		    	if (dx < 0)
1873		    	{
1874		    	    lcapFace.xa = -vertices[V_LEFT].x;
1875		    	    lcapFace.ya = -vertices[V_LEFT].y;
1876			    lcapFace.k = slopes[V_LEFT].k;
1877		    	}
1878		    	else
1879		    	{
1880		    	    lcapFace.xa = vertices[V_TOP].x;
1881		    	    lcapFace.ya = vertices[V_TOP].y;
1882			    lcapFace.k = -slopes[V_LEFT].k;
1883		    	}
1884		    	miLineArc (pDrawable, pGC, pixel, spanData,
1885			       	   &lcapFace, (LineFacePtr) NULL,
1886			       	   lcenterx, lcentery, FALSE);
1887		    }
1888		    if (dx < 0)
1889		    {
1890		    	rcapFace.xa = vertices[V_BOTTOM].x;
1891		    	rcapFace.ya = vertices[V_BOTTOM].y;
1892			rcapFace.k = slopes[V_RIGHT].k;
1893		    }
1894		    else
1895		    {
1896		    	rcapFace.xa = -vertices[V_RIGHT].x;
1897		    	rcapFace.ya = -vertices[V_RIGHT].y;
1898			rcapFace.k = -slopes[V_RIGHT].k;
1899		    }
1900		    miLineArc (pDrawable, pGC, pixel, spanData,
1901			       (LineFacePtr) NULL, &rcapFace,
1902			       rcenterx, rcentery, FALSE);
1903		    break;
1904	    	}
1905	    }
1906	}
1907	LRemain -= dashRemain;
1908	++dashIndex;
1909	if (dashIndex == pGC->numInDashList)
1910	    dashIndex = 0;
1911	dashRemain = pDash[dashIndex];
1912
1913	lcenterx = rcenterx;
1914	lcentery = rcentery;
1915
1916	vertices[V_TOP] = vertices[V_RIGHT];
1917	vertices[V_LEFT] = vertices[V_BOTTOM];
1918	slopes[V_LEFT].k = -slopes[V_RIGHT].k;
1919	first = FALSE;
1920    }
1921
1922    if (pGC->lineStyle == LineDoubleDash || !(dashIndex & 1))
1923    {
1924    	vertices[V_TOP].x -= dx;
1925    	vertices[V_TOP].y -= dy;
1926
1927	vertices[V_LEFT].x -= dx;
1928	vertices[V_LEFT].y -= dy;
1929
1930	vertices[V_RIGHT].x = rdy;
1931	vertices[V_RIGHT].y = -rdx;
1932
1933	vertices[V_BOTTOM].x = -rdy;
1934	vertices[V_BOTTOM].y = rdx;
1935
1936
1937	if (projectRight)
1938	{
1939	    vertices[V_RIGHT].x += rdx;
1940	    vertices[V_RIGHT].y += rdy;
1941
1942	    vertices[V_BOTTOM].x += rdx;
1943	    vertices[V_BOTTOM].y += rdy;
1944	    slopes[V_RIGHT].k = vertices[V_RIGHT].x *
1945				slopes[V_RIGHT].dy -
1946				vertices[V_RIGHT].y *
1947				slopes[V_RIGHT].dx;
1948	}
1949	else
1950	    slopes[V_RIGHT].k = 0;
1951
1952	if (!first && pGC->lineStyle == LineOnOffDash &&
1953	    pGC->capStyle == CapProjecting)
1954	{
1955	    vertices[V_TOP].x -= rdx;
1956	    vertices[V_TOP].y -= rdy;
1957
1958	    vertices[V_LEFT].x -= rdx;
1959	    vertices[V_LEFT].y -= rdy;
1960	    slopes[V_LEFT].k = vertices[V_LEFT].x *
1961			       slopes[V_LEFT].dy -
1962			       vertices[V_LEFT].y *
1963			       slopes[V_LEFT].dx;
1964	}
1965	else
1966	    slopes[V_LEFT].k += dx * dx + dy * dy;
1967
1968
1969	y = miPolyBuildPoly (vertices, slopes, 4, x2, y2,
1970			     left, right, &nleft, &nright, &h);
1971
1972	pixel = (dashIndex & 1) ? pGC->bgPixel : pGC->fgPixel;
1973	miFillPolyHelper (pDrawable, pGC, pixel, spanData, y, h, left, right, nleft, nright);
1974	if (!first && pGC->lineStyle == LineOnOffDash &&
1975	    pGC->capStyle == CapRound)
1976	{
1977	    lcapFace.x = x2;
1978	    lcapFace.y = y2;
1979	    if (dx < 0)
1980	    {
1981		lcapFace.xa = -vertices[V_LEFT].x;
1982		lcapFace.ya = -vertices[V_LEFT].y;
1983		lcapFace.k = slopes[V_LEFT].k;
1984	    }
1985	    else
1986	    {
1987		lcapFace.xa = vertices[V_TOP].x;
1988		lcapFace.ya = vertices[V_TOP].y;
1989		lcapFace.k = -slopes[V_LEFT].k;
1990	    }
1991	    miLineArc (pDrawable, pGC, pixel, spanData,
1992		       &lcapFace, (LineFacePtr) NULL,
1993		       rcenterx, rcentery, FALSE);
1994	}
1995    }
1996    dashRemain = ((double) dashRemain) - LRemain;
1997    if (dashRemain == 0)
1998    {
1999	dashIndex++;
2000	if (dashIndex == pGC->numInDashList)
2001	    dashIndex = 0;
2002	dashRemain = pDash[dashIndex];
2003    }
2004
2005    leftFace->x = x1;
2006    leftFace->y = y1;
2007    leftFace->dx = dx;
2008    leftFace->dy = dy;
2009    leftFace->xa = rdy;
2010    leftFace->ya = -rdx;
2011    leftFace->k = k;
2012
2013    rightFace->x = x2;
2014    rightFace->y = y2;
2015    rightFace->dx = -dx;
2016    rightFace->dy = -dy;
2017    rightFace->xa = -rdy;
2018    rightFace->ya = rdx;
2019    rightFace->k = k;
2020
2021    *pDashIndex = dashIndex;
2022    *pDashOffset = pDash[dashIndex] - dashRemain;
2023}
2024
2025_X_EXPORT void
2026miWideDash (pDrawable, pGC, mode, npt, pPts)
2027    DrawablePtr	pDrawable;
2028    GCPtr 	pGC;
2029    int		mode;
2030    int 	npt;
2031    DDXPointPtr pPts;
2032{
2033    int			x1, y1, x2, y2;
2034    unsigned long	pixel;
2035    Bool		projectLeft, projectRight;
2036    LineFaceRec		leftFace, rightFace, prevRightFace;
2037    LineFaceRec		firstFace;
2038    int			first;
2039    int			dashIndex, dashOffset;
2040    int			prevDashIndex;
2041    SpanDataRec		spanDataRec;
2042    SpanDataPtr		spanData;
2043    Bool		somethingDrawn = FALSE;
2044    Bool		selfJoin;
2045    Bool		endIsFg = FALSE, startIsFg = FALSE;
2046    Bool		firstIsFg = FALSE, prevIsFg = FALSE;
2047
2048#if 0
2049    /* XXX backward compatibility */
2050    if (pGC->lineWidth == 0)
2051    {
2052	miZeroDashLine (pDrawable, pGC, mode, npt, pPts);
2053	return;
2054    }
2055#endif
2056    if (pGC->lineStyle == LineDoubleDash &&
2057	(pGC->fillStyle == FillOpaqueStippled || pGC->fillStyle == FillTiled))
2058    {
2059	miWideLine (pDrawable, pGC, mode, npt, pPts);
2060	return;
2061    }
2062    if (npt == 0)
2063	return;
2064    spanData = miSetupSpanData (pGC, &spanDataRec, npt);
2065    x2 = pPts->x;
2066    y2 = pPts->y;
2067    first = TRUE;
2068    selfJoin = FALSE;
2069    if (mode == CoordModePrevious)
2070    {
2071	int nptTmp;
2072	DDXPointPtr pPtsTmp;
2073
2074	x1 = x2;
2075	y1 = y2;
2076	nptTmp = npt;
2077	pPtsTmp = pPts + 1;
2078	while (--nptTmp)
2079	{
2080	    x1 += pPtsTmp->x;
2081	    y1 += pPtsTmp->y;
2082	    ++pPtsTmp;
2083	}
2084	if (x2 == x1 && y2 == y1)
2085	    selfJoin = TRUE;
2086    }
2087    else if (x2 == pPts[npt-1].x && y2 == pPts[npt-1].y)
2088    {
2089	selfJoin = TRUE;
2090    }
2091    projectLeft = pGC->capStyle == CapProjecting && !selfJoin;
2092    projectRight = FALSE;
2093    dashIndex = 0;
2094    dashOffset = 0;
2095    miStepDash ((int)pGC->dashOffset, &dashIndex,
2096	        pGC->dash, (int)pGC->numInDashList, &dashOffset);
2097    while (--npt)
2098    {
2099	x1 = x2;
2100	y1 = y2;
2101	++pPts;
2102	x2 = pPts->x;
2103	y2 = pPts->y;
2104	if (mode == CoordModePrevious)
2105	{
2106	    x2 += x1;
2107	    y2 += y1;
2108	}
2109	if (x1 != x2 || y1 != y2)
2110	{
2111	    somethingDrawn = TRUE;
2112	    if (npt == 1 && pGC->capStyle == CapProjecting &&
2113		(!selfJoin || !firstIsFg))
2114		projectRight = TRUE;
2115	    prevDashIndex = dashIndex;
2116	    miWideDashSegment (pDrawable, pGC, spanData, &dashOffset, &dashIndex,
2117				x1, y1, x2, y2,
2118				projectLeft, projectRight, &leftFace, &rightFace);
2119	    startIsFg = !(prevDashIndex & 1);
2120	    endIsFg = (dashIndex & 1) ^ (dashOffset != 0);
2121	    if (pGC->lineStyle == LineDoubleDash || startIsFg)
2122	    {
2123	    	pixel = startIsFg ? pGC->fgPixel : pGC->bgPixel;
2124	    	if (first || (pGC->lineStyle == LineOnOffDash && !prevIsFg))
2125	    	{
2126	    	    if (first && selfJoin)
2127		    {
2128		    	firstFace = leftFace;
2129			firstIsFg = startIsFg;
2130		    }
2131	    	    else if (pGC->capStyle == CapRound)
2132		    	miLineArc (pDrawable, pGC, pixel, spanData,
2133			       	   &leftFace, (LineFacePtr) NULL,
2134			       	   (double)0.0, (double)0.0, TRUE);
2135	    	}
2136	    	else
2137	    	{
2138	    	    miLineJoin (pDrawable, pGC, pixel, spanData, &leftFace,
2139		            	&prevRightFace);
2140	    	}
2141	    }
2142	    prevRightFace = rightFace;
2143	    prevIsFg = endIsFg;
2144	    first = FALSE;
2145	    projectLeft = FALSE;
2146	}
2147	if (npt == 1 && somethingDrawn)
2148	{
2149	    if (pGC->lineStyle == LineDoubleDash || endIsFg)
2150	    {
2151		pixel = endIsFg ? pGC->fgPixel : pGC->bgPixel;
2152		if (selfJoin && (pGC->lineStyle == LineDoubleDash || firstIsFg))
2153		{
2154		    miLineJoin (pDrawable, pGC, pixel, spanData, &firstFace,
2155				&rightFace);
2156		}
2157		else
2158		{
2159		    if (pGC->capStyle == CapRound)
2160			miLineArc (pDrawable, pGC, pixel, spanData,
2161				    (LineFacePtr) NULL, &rightFace,
2162				    (double)0.0, (double)0.0, TRUE);
2163		}
2164	    }
2165	    else
2166	    {
2167		/* glue a cap to the start of the line if
2168		 * we're OnOffDash and ended on odd dash
2169		 */
2170		if (selfJoin && firstIsFg)
2171		{
2172		    pixel = pGC->fgPixel;
2173		    if (pGC->capStyle == CapProjecting)
2174			miLineProjectingCap (pDrawable, pGC, pixel, spanData,
2175				    &firstFace, TRUE,
2176				    (double)0.0, (double)0.0, TRUE);
2177		    else if (pGC->capStyle == CapRound)
2178			miLineArc (pDrawable, pGC, pixel, spanData,
2179				    &firstFace, (LineFacePtr) NULL,
2180				    (double)0.0, (double)0.0, TRUE);
2181		}
2182	    }
2183	}
2184    }
2185    /* handle crock where all points are coincident */
2186    if (!somethingDrawn && (pGC->lineStyle == LineDoubleDash || !(dashIndex & 1)))
2187    {
2188	/* not the same as endIsFg computation above */
2189	pixel = (dashIndex & 1) ? pGC->bgPixel : pGC->fgPixel;
2190	switch (pGC->capStyle) {
2191	case CapRound:
2192	    miLineArc (pDrawable, pGC, pixel, spanData,
2193		       (LineFacePtr) NULL, (LineFacePtr) NULL,
2194		       (double)x2, (double)y2,
2195		       FALSE);
2196	    break;
2197	case CapProjecting:
2198	    x1 = pGC->lineWidth;
2199	    miFillRectPolyHelper (pDrawable, pGC, pixel, spanData,
2200				  x2 - (x1 >> 1), y2 - (x1 >> 1), x1, x1);
2201	    break;
2202	}
2203    }
2204    if (spanData)
2205	miCleanupSpanData (pDrawable, pGC, spanData);
2206}
2207