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