11f0ac6a5Smrg/*
21f0ac6a5Smrg *
31f0ac6a5Smrg * Copyright © 2002 Keith Packard
41f0ac6a5Smrg *
51f0ac6a5Smrg * Permission to use, copy, modify, distribute, and sell this software and its
61f0ac6a5Smrg * documentation for any purpose is hereby granted without fee, provided that
71f0ac6a5Smrg * the above copyright notice appear in all copies and that both that
81f0ac6a5Smrg * copyright notice and this permission notice appear in supporting
91f0ac6a5Smrg * documentation, and that the name of Keith Packard not be used in
101f0ac6a5Smrg * advertising or publicity pertaining to distribution of the software without
111f0ac6a5Smrg * specific, written prior permission.  Keith Packard makes no
121f0ac6a5Smrg * representations about the suitability of this software for any purpose.  It
131f0ac6a5Smrg * is provided "as is" without express or implied warranty.
141f0ac6a5Smrg *
151f0ac6a5Smrg * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
161f0ac6a5Smrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
171f0ac6a5Smrg * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
181f0ac6a5Smrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
191f0ac6a5Smrg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
201f0ac6a5Smrg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
211f0ac6a5Smrg * PERFORMANCE OF THIS SOFTWARE.
221f0ac6a5Smrg */
231f0ac6a5Smrg
241f0ac6a5Smrg#ifdef HAVE_CONFIG_H
251f0ac6a5Smrg#include <config.h>
261f0ac6a5Smrg#endif
271f0ac6a5Smrg#include "Xrenderint.h"
281f0ac6a5Smrg
291f0ac6a5Smrgtypedef struct _Edge Edge;
301f0ac6a5Smrg
311f0ac6a5Smrgstruct _Edge {
321f0ac6a5Smrg    XLineFixed	edge;
331f0ac6a5Smrg    XFixed	current_x;
341f0ac6a5Smrg    Bool	clockWise;
351f0ac6a5Smrg    Edge	*next, *prev;
361f0ac6a5Smrg};
371f0ac6a5Smrg
381f0ac6a5Smrgstatic int
391f0ac6a5SmrgCompareEdge (const void *o1, const void *o2)
401f0ac6a5Smrg{
411f0ac6a5Smrg    const Edge	*e1 = o1, *e2 = o2;
421f0ac6a5Smrg
431f0ac6a5Smrg    return e1->edge.p1.y - e2->edge.p1.y;
441f0ac6a5Smrg}
451f0ac6a5Smrg
461f0ac6a5Smrgstatic XFixed
471f0ac6a5SmrgXRenderComputeX (XLineFixed *line, XFixed y)
481f0ac6a5Smrg{
491f0ac6a5Smrg    XFixed  dx = line->p2.x - line->p1.x;
501f0ac6a5Smrg    double  ex = (double) (y - line->p1.y) * (double) dx;
511f0ac6a5Smrg    XFixed  dy = line->p2.y - line->p1.y;
521f0ac6a5Smrg
531f0ac6a5Smrg    return (XFixed) line->p1.x + (XFixed) (ex / dy);
541f0ac6a5Smrg}
551f0ac6a5Smrg
561f0ac6a5Smrgstatic double
571f0ac6a5SmrgXRenderComputeInverseSlope (XLineFixed *l)
581f0ac6a5Smrg{
596fae4e5dSmrg    return (XFixedToDouble (l->p2.x - l->p1.x) /
601f0ac6a5Smrg	    XFixedToDouble (l->p2.y - l->p1.y));
611f0ac6a5Smrg}
621f0ac6a5Smrg
631f0ac6a5Smrgstatic double
641f0ac6a5SmrgXRenderComputeXIntercept (XLineFixed *l, double inverse_slope)
651f0ac6a5Smrg{
661f0ac6a5Smrg    return XFixedToDouble (l->p1.x) - inverse_slope * XFixedToDouble (l->p1.y);
671f0ac6a5Smrg}
681f0ac6a5Smrg
691f0ac6a5Smrgstatic XFixed
701f0ac6a5SmrgXRenderComputeIntersect (XLineFixed *l1, XLineFixed *l2)
711f0ac6a5Smrg{
721f0ac6a5Smrg    /*
731f0ac6a5Smrg     * x = m1y + b1
741f0ac6a5Smrg     * x = m2y + b2
751f0ac6a5Smrg     * m1y + b1 = m2y + b2
761f0ac6a5Smrg     * y * (m1 - m2) = b2 - b1
771f0ac6a5Smrg     * y = (b2 - b1) / (m1 - m2)
781f0ac6a5Smrg     */
791f0ac6a5Smrg    double  m1 = XRenderComputeInverseSlope (l1);
801f0ac6a5Smrg    double  b1 = XRenderComputeXIntercept (l1, m1);
811f0ac6a5Smrg    double  m2 = XRenderComputeInverseSlope (l2);
821f0ac6a5Smrg    double  b2 = XRenderComputeXIntercept (l2, m2);
831f0ac6a5Smrg
84d21ab8bcSmrg    if ( m1 == m2 ) return XDoubleToFixed(32766); /* lines don't intersect */
85d21ab8bcSmrg
861f0ac6a5Smrg    return XDoubleToFixed ((b2 - b1) / (m1 - m2));
871f0ac6a5Smrg}
881f0ac6a5Smrg
891f0ac6a5Smrgstatic int
901f0ac6a5SmrgXRenderComputeTrapezoids (Edge		*edges,
911f0ac6a5Smrg			  int		nedges,
92d21ab8bcSmrg			  int		winding _X_UNUSED,
93d21ab8bcSmrg			  XTrapezoid	*traps,
94d21ab8bcSmrg			  int           *ntraps,
95d21ab8bcSmrg			  int           maxtraps)
961f0ac6a5Smrg{
97d21ab8bcSmrg    int		ok = 1, inactive;
981f0ac6a5Smrg    Edge	*active;
991f0ac6a5Smrg    Edge	*e, *en, *next;
1001f0ac6a5Smrg    XFixed	y, next_y, intersect;
1016fae4e5dSmrg
102d21ab8bcSmrg    qsort (edges, (size_t) nedges, sizeof (Edge), CompareEdge);
1036fae4e5dSmrg
104d21ab8bcSmrg    *ntraps = 0;
1051f0ac6a5Smrg    y = edges[0].edge.p1.y;
106e5410a46Smrg    active = NULL;
1071f0ac6a5Smrg    inactive = 0;
1081f0ac6a5Smrg    while (active || inactive < nedges)
1091f0ac6a5Smrg    {
1101f0ac6a5Smrg	/* insert new active edges into list */
1111f0ac6a5Smrg	while (inactive < nedges)
1121f0ac6a5Smrg	{
1131f0ac6a5Smrg	    e = &edges[inactive];
1141f0ac6a5Smrg	    if (e->edge.p1.y > y)
1151f0ac6a5Smrg		break;
1161f0ac6a5Smrg	    /* move this edge into the active list */
1171f0ac6a5Smrg	    inactive++;
1181f0ac6a5Smrg	    e->next = active;
119e5410a46Smrg	    e->prev = NULL;
1201f0ac6a5Smrg	    if (active)
1211f0ac6a5Smrg		active->prev = e;
1221f0ac6a5Smrg	    active = e;
1231f0ac6a5Smrg	}
1241f0ac6a5Smrg	/* compute x coordinates along this group */
1251f0ac6a5Smrg	for (e = active; e; e = e->next)
1261f0ac6a5Smrg	    e->current_x = XRenderComputeX (&e->edge, y);
1276fae4e5dSmrg
1281f0ac6a5Smrg	/* sort active list */
1291f0ac6a5Smrg	for (e = active; e; e = next)
1301f0ac6a5Smrg	{
1311f0ac6a5Smrg	    next = e->next;
1321f0ac6a5Smrg	    /*
1331f0ac6a5Smrg	     * Find one later in the list that belongs before the
1341f0ac6a5Smrg	     * current one
1351f0ac6a5Smrg	     */
1361f0ac6a5Smrg	    for (en = next; en; en = en->next)
1371f0ac6a5Smrg	    {
1381f0ac6a5Smrg		if (en->current_x < e->current_x ||
1391f0ac6a5Smrg		    (en->current_x == e->current_x &&
1401f0ac6a5Smrg		     en->edge.p2.x < e->edge.p2.x))
1411f0ac6a5Smrg		{
1421f0ac6a5Smrg		    /*
1431f0ac6a5Smrg		     * insert en before e
1441f0ac6a5Smrg		     *
1451f0ac6a5Smrg		     * extract en
1461f0ac6a5Smrg		     */
1471f0ac6a5Smrg		    en->prev->next = en->next;
1481f0ac6a5Smrg		    if (en->next)
1491f0ac6a5Smrg			en->next->prev = en->prev;
1501f0ac6a5Smrg		    /*
1511f0ac6a5Smrg		     * insert en
1521f0ac6a5Smrg		     */
1531f0ac6a5Smrg		    if (e->prev)
1541f0ac6a5Smrg			e->prev->next = en;
1551f0ac6a5Smrg		    else
1561f0ac6a5Smrg			active = en;
1571f0ac6a5Smrg		    en->prev = e->prev;
1581f0ac6a5Smrg		    e->prev = en;
1591f0ac6a5Smrg		    en->next = e;
1601f0ac6a5Smrg		    /*
1611f0ac6a5Smrg		     * start over at en
1621f0ac6a5Smrg		     */
1631f0ac6a5Smrg		    next = en;
1641f0ac6a5Smrg		    break;
1651f0ac6a5Smrg		}
1661f0ac6a5Smrg	    }
1671f0ac6a5Smrg	}
1681f0ac6a5Smrg#if 0
1691f0ac6a5Smrg	printf ("y: %6.3g:", y / 65536.0);
1701f0ac6a5Smrg	for (e = active; e; e = e->next)
1711f0ac6a5Smrg	{
1721f0ac6a5Smrg	    printf (" %6.3g", e->current_x / 65536.0);
1731f0ac6a5Smrg	}
1741f0ac6a5Smrg	printf ("\n");
1751f0ac6a5Smrg#endif
1761f0ac6a5Smrg	/* find next inflection point */
1771f0ac6a5Smrg	next_y = active->edge.p2.y;
1781f0ac6a5Smrg	for (e = active; e; e = en)
1791f0ac6a5Smrg	{
1801f0ac6a5Smrg	    if (e->edge.p2.y < next_y)
1811f0ac6a5Smrg		next_y = e->edge.p2.y;
1821f0ac6a5Smrg	    en = e->next;
1831f0ac6a5Smrg	    /* check intersect */
1846fae4e5dSmrg	    if (en && e->edge.p2.x > en->edge.p2.x)
1851f0ac6a5Smrg	    {
1861f0ac6a5Smrg		intersect = XRenderComputeIntersect (&e->edge, &e->next->edge);
1871f0ac6a5Smrg		/* make sure this point is below the actual intersection */
1881f0ac6a5Smrg		intersect = intersect + 1;
189d21ab8bcSmrg		if (intersect < next_y && intersect > y)
1901f0ac6a5Smrg		    next_y = intersect;
1911f0ac6a5Smrg	    }
1921f0ac6a5Smrg	}
1931f0ac6a5Smrg	/* check next inactive point */
1941f0ac6a5Smrg	if (inactive < nedges && edges[inactive].edge.p1.y < next_y)
1951f0ac6a5Smrg	    next_y = edges[inactive].edge.p1.y;
1966fae4e5dSmrg
1971f0ac6a5Smrg	/* walk the list generating trapezoids */
1981f0ac6a5Smrg	for (e = active; e && (en = e->next); e = en->next)
1991f0ac6a5Smrg	{
2001f0ac6a5Smrg	    traps->top = y;
2011f0ac6a5Smrg	    traps->bottom = next_y;
2021f0ac6a5Smrg	    traps->left = e->edge;
2031f0ac6a5Smrg	    traps->right = en->edge;
2041f0ac6a5Smrg	    traps++;
205d21ab8bcSmrg	    (*ntraps)++;
206d21ab8bcSmrg            if ( --maxtraps <= 0 ) {
207d21ab8bcSmrg	       ok = 0;
208d21ab8bcSmrg	       break;
209d21ab8bcSmrg	    }
2101f0ac6a5Smrg	}
2111f0ac6a5Smrg
2121f0ac6a5Smrg	y = next_y;
2136fae4e5dSmrg
2141f0ac6a5Smrg	/* delete inactive edges from list */
2151f0ac6a5Smrg	for (e = active; e; e = next)
2161f0ac6a5Smrg	{
2171f0ac6a5Smrg	    next = e->next;
2181f0ac6a5Smrg	    if (e->edge.p2.y <= y)
2191f0ac6a5Smrg	    {
2201f0ac6a5Smrg		if (e->prev)
2211f0ac6a5Smrg		    e->prev->next = e->next;
2221f0ac6a5Smrg		else
2231f0ac6a5Smrg		    active = e->next;
2241f0ac6a5Smrg		if (e->next)
2251f0ac6a5Smrg		    e->next->prev = e->prev;
2261f0ac6a5Smrg	    }
2271f0ac6a5Smrg	}
2281f0ac6a5Smrg    }
229d21ab8bcSmrg    return ok;
2301f0ac6a5Smrg}
2311f0ac6a5Smrg
2321f0ac6a5Smrgvoid
2331f0ac6a5SmrgXRenderCompositeDoublePoly (Display		    *dpy,
2341f0ac6a5Smrg			    int			    op,
2351f0ac6a5Smrg			    Picture		    src,
2361f0ac6a5Smrg			    Picture		    dst,
2371f0ac6a5Smrg			    _Xconst XRenderPictFormat	*maskFormat,
2381f0ac6a5Smrg			    int			    xSrc,
2391f0ac6a5Smrg			    int			    ySrc,
240d21ab8bcSmrg			    int			    xDst _X_UNUSED,
241d21ab8bcSmrg			    int			    yDst _X_UNUSED,
2421f0ac6a5Smrg			    _Xconst XPointDouble    *fpoints,
2431f0ac6a5Smrg			    int			    npoints,
2441f0ac6a5Smrg			    int			    winding)
2451f0ac6a5Smrg{
2461f0ac6a5Smrg    Edge	    *edges;
2471f0ac6a5Smrg    XTrapezoid	    *traps;
2481f0ac6a5Smrg    int		    i, nedges, ntraps;
249d21ab8bcSmrg    XFixed	    prevx = 0, prevy = 0, firstx = 0, firsty = 0;
2501f0ac6a5Smrg    XFixed	    top = 0, bottom = 0;	/* GCCism */
2511f0ac6a5Smrg
252d21ab8bcSmrg    edges = Xmalloc (((size_t) npoints * sizeof (Edge)) +
253d21ab8bcSmrg                     ((size_t) (npoints * npoints) * sizeof (XTrapezoid)));
2541f0ac6a5Smrg    if (!edges)
2551f0ac6a5Smrg	return;
2561f0ac6a5Smrg    traps = (XTrapezoid *) (edges + npoints);
2571f0ac6a5Smrg    nedges = 0;
2581f0ac6a5Smrg    for (i = 0; i <= npoints; i++)
2591f0ac6a5Smrg    {
260d21ab8bcSmrg	XFixed	x, y;
261d21ab8bcSmrg
2621f0ac6a5Smrg	if (i == npoints)
2631f0ac6a5Smrg	{
2641f0ac6a5Smrg	    x = firstx;
2651f0ac6a5Smrg	    y = firsty;
2661f0ac6a5Smrg	}
2671f0ac6a5Smrg	else
2681f0ac6a5Smrg	{
2691f0ac6a5Smrg	    x = XDoubleToFixed (fpoints[i].x);
2701f0ac6a5Smrg	    y = XDoubleToFixed (fpoints[i].y);
2711f0ac6a5Smrg	}
2721f0ac6a5Smrg	if (i)
2731f0ac6a5Smrg	{
2741f0ac6a5Smrg	    if (y < top)
2751f0ac6a5Smrg		top = y;
2761f0ac6a5Smrg	    else if (y > bottom)
2771f0ac6a5Smrg		bottom = y;
2781f0ac6a5Smrg	    if (prevy < y)
2791f0ac6a5Smrg	    {
2801f0ac6a5Smrg		edges[nedges].edge.p1.x = prevx;
2811f0ac6a5Smrg		edges[nedges].edge.p1.y = prevy;
2821f0ac6a5Smrg		edges[nedges].edge.p2.x = x;
2831f0ac6a5Smrg		edges[nedges].edge.p2.y = y;
2841f0ac6a5Smrg		edges[nedges].clockWise = True;
2851f0ac6a5Smrg		nedges++;
2861f0ac6a5Smrg	    }
2871f0ac6a5Smrg	    else if (prevy > y)
2881f0ac6a5Smrg	    {
2891f0ac6a5Smrg		edges[nedges].edge.p1.x = x;
2901f0ac6a5Smrg		edges[nedges].edge.p1.y = y;
2911f0ac6a5Smrg		edges[nedges].edge.p2.x = prevx;
2921f0ac6a5Smrg		edges[nedges].edge.p2.y = prevy;
2931f0ac6a5Smrg		edges[nedges].clockWise = False;
2941f0ac6a5Smrg		nedges++;
2951f0ac6a5Smrg	    }
2961f0ac6a5Smrg	    /* drop horizontal edges */
2971f0ac6a5Smrg	}
2981f0ac6a5Smrg	else
2991f0ac6a5Smrg	{
3001f0ac6a5Smrg	    top = y;
3011f0ac6a5Smrg	    bottom = y;
3021f0ac6a5Smrg	    firstx = x;
3031f0ac6a5Smrg	    firsty = y;
3041f0ac6a5Smrg	}
3051f0ac6a5Smrg	prevx = x;
3061f0ac6a5Smrg	prevy = y;
3071f0ac6a5Smrg    }
308d21ab8bcSmrg    if ( XRenderComputeTrapezoids (edges, nedges, winding, traps, &ntraps, npoints * npoints )) {
309d21ab8bcSmrg       /* XXX adjust xSrc/xDst */
310d21ab8bcSmrg       XRenderCompositeTrapezoids (dpy, op, src, dst, maskFormat, xSrc, ySrc, traps, ntraps);
311d21ab8bcSmrg    }
3121f0ac6a5Smrg    Xfree (edges);
3131f0ac6a5Smrg}
314