src/gui/painting/qtessellator.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gui/painting/qtessellator.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,1498 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtessellator_p.h"
+
+#include <QRect>
+#include <QList>
+#include <QDebug>
+
+#include <qmath.h>
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+//#define DEBUG
+#ifdef DEBUG
+#define QDEBUG qDebug
+#else
+#define QDEBUG if (1){} else qDebug
+#endif
+
+static const bool emit_clever = true;
+static const bool mark_clever = false;
+
+enum VertexFlags {
+    LineBeforeStarts = 0x1,
+    LineBeforeEnds = 0x2,
+    LineBeforeHorizontal = 0x4,
+    LineAfterStarts = 0x8,
+    LineAfterEnds = 0x10,
+    LineAfterHorizontal = 0x20
+};
+
+
+
+class QTessellatorPrivate {
+public:
+    struct Vertices;
+
+    QTessellatorPrivate() {}
+
+    QRectF collectAndSortVertices(const QPointF *points, int *maxActiveEdges);
+    void cancelCoincidingEdges();
+
+    void emitEdges(QTessellator *tessellator);
+    void processIntersections();
+    void removeEdges();
+    void addEdges();
+    void addIntersections();
+
+    struct Vertex : public QTessellator::Vertex
+    {
+        int flags;
+    };
+
+    struct Intersection
+    {
+        Q27Dot5 y;
+        int edge;
+        bool operator <(const Intersection &other) const {
+            if (y != other.y)
+                return y < other.y;
+            return edge < other.edge;
+        }
+    };
+    struct IntersectionLink
+    {
+        int next;
+        int prev;
+    };
+    typedef QMap<Intersection, IntersectionLink> Intersections;
+
+    struct Edge {
+        Edge(const Vertices &v, int _edge);
+        int edge;
+        const Vertex *v0;
+        const Vertex *v1;
+        Q27Dot5 y_left;
+        Q27Dot5 y_right;
+        signed int winding : 8;
+        bool mark;
+        bool free;
+        bool intersect_left;
+        bool intersect_right;
+        bool isLeftOf(const Edge &other, Q27Dot5 y) const;
+        Q27Dot5 positionAt(Q27Dot5 y) const;
+        bool intersect(const Edge &other, Q27Dot5 *y, bool *det_positive) const;
+
+    };
+
+    class EdgeSorter
+    {
+    public:
+        EdgeSorter(int _y) : y(_y) {}
+        bool operator() (const Edge *e1, const Edge *e2);
+        int y;
+    };
+
+    class Scanline {
+    public:
+        Scanline();
+        ~Scanline();
+
+        void init(int maxActiveEdges);
+        void done();
+
+        int findEdgePosition(Q27Dot5 x, Q27Dot5 y) const;
+        int findEdgePosition(const Edge &e) const;
+        int findEdge(int edge) const;
+        void clearMarks();
+
+        void swap(int p1, int p2) {
+            Edge *tmp = edges[p1];
+            edges[p1] = edges[p2];
+            edges[p2] = tmp;
+        }
+        void insert(int pos, const Edge &e);
+        void removeAt(int pos);
+        void markEdges(int pos1, int pos2);
+
+        void prepareLine();
+        void lineDone();
+
+        Edge **old;
+        int old_size;
+
+        Edge **edges;
+        int size;
+
+    private:
+        Edge *edge_table;
+        int first_unused;
+        int max_edges;
+        enum { default_alloc = 32 };
+    };
+
+    struct Vertices {
+        enum { default_alloc = 128 };
+        Vertices();
+        ~Vertices();
+        void init(int maxVertices);
+        void done();
+        Vertex *storage;
+        Vertex **sorted;
+
+        Vertex *operator[] (int i) { return storage + i; }
+        const Vertex *operator[] (int i) const { return storage + i; }
+        int position(const Vertex *v) const {
+            return v - storage;
+        }
+        Vertex *next(Vertex *v) {
+            ++v;
+            if (v == storage + nPoints)
+                v = storage;
+            return v;
+        }
+        const Vertex *next(const Vertex *v) const {
+            ++v;
+            if (v == storage + nPoints)
+                v = storage;
+            return v;
+        }
+        int nextPos(const Vertex *v) const {
+            ++v;
+            if (v == storage + nPoints)
+                return 0;
+            return v - storage;
+        }
+        Vertex *prev(Vertex *v) {
+            if (v == storage)
+                v = storage + nPoints;
+            --v;
+            return v;
+        }
+        const Vertex *prev(const Vertex *v) const {
+            if (v == storage)
+                v = storage + nPoints;
+            --v;
+            return v;
+        }
+        int prevPos(const Vertex *v) const {
+            if (v == storage)
+                v = storage + nPoints;
+            --v;
+            return v - storage;
+        }
+        int nPoints;
+        int allocated;
+    };
+    Vertices vertices;
+    Intersections intersections;
+    Scanline scanline;
+    bool winding;
+    Q27Dot5 y;
+    int currentVertex;
+
+private:
+    void addIntersection(const Edge *e1, const Edge *e2);
+    bool edgeInChain(Intersection i, int edge);
+};
+
+
+QTessellatorPrivate::Edge::Edge(const QTessellatorPrivate::Vertices &vertices, int edge)
+{
+    this->edge = edge;
+    intersect_left = intersect_right = true;
+    mark = false;
+    free = false;
+
+    v0 = vertices[edge];
+    v1 = vertices.next(v0);
+
+    Q_ASSERT(v0->y != v1->y);
+
+    if (v0->y > v1->y) {
+        qSwap(v0, v1);
+        winding = -1;
+    } else {
+        winding = 1;
+    }
+    y_left = y_right = v0->y;
+}
+
+// This is basically the algorithm from graphics gems. The algorithm
+// is cubic in the coordinates at one place.  Since we use 64bit
+// integers, this implies, that the allowed range for our coordinates
+// is limited to 21 bits.  With 5 bits behind the decimal, this
+// implies that differences in coordaintes can range from 2*SHORT_MIN
+// to 2*SHORT_MAX, giving us efficiently a coordinate system from
+// SHORT_MIN to SHORT_MAX.
+//
+
+// WARNING: It's absolutely critical that the intersect() and isLeftOf() methods use
+// exactly the same algorithm to calulate yi. It's also important to be sure the algorithms
+// are transitive (ie. the conditions below are true for all input data):
+//
+// a.intersect(b) == b.intersect(a)
+// a.isLeftOf(b) != b.isLeftOf(a)
+//
+// This is tricky to get right, so be very careful when changing anything in here!
+
+static inline bool sameSign(qint64 a, qint64 b) {
+    return (((qint64) ((quint64) a ^ (quint64) b)) >= 0 );
+}
+
+bool QTessellatorPrivate::Edge::intersect(const Edge &other, Q27Dot5 *y, bool *det_positive) const
+{
+    qint64 a1 = v1->y - v0->y;
+    qint64 b1 = v0->x - v1->x;
+
+    qint64 a2 = other.v1->y - other.v0->y;
+    qint64 b2 = other.v0->x - other.v1->x;
+
+    qint64 det = a1 * b2 - a2 * b1;
+    if (det == 0)
+        return false;
+
+    qint64 c1 = qint64(v1->x) * v0->y - qint64(v0->x) * v1->y;
+
+    qint64 r3 = a1 * other.v0->x + b1 * other.v0->y + c1;
+    qint64 r4 = a1 * other.v1->x + b1 * other.v1->y + c1;
+
+    // Check signs of r3 and r4.  If both point 3 and point 4 lie on
+    // same side of line 1, the line segments do not intersect.
+    QDEBUG() << "        " << r3 << r4;
+    if (r3 != 0 && r4 != 0 && sameSign( r3, r4 ))
+        return false;
+
+    qint64 c2 = qint64(other.v1->x) * other.v0->y - qint64(other.v0->x) * other.v1->y;
+
+    qint64 r1 = a2 * v0->x + b2 * v0->y + c2;
+    qint64 r2 = a2 * v1->x + b2 * v1->y + c2;
+
+    // Check signs of r1 and r2.  If both point 1 and point 2 lie
+    // on same side of second line segment, the line segments do not intersect.
+    QDEBUG() << "        " << r1 << r2;
+    if (r1 != 0 && r2 != 0 && sameSign( r1, r2 ))
+        return false;
+
+    // The det/2 is to get rounding instead of truncating.  It
+    // is added or subtracted to the numerator, depending upon the
+    // sign of the numerator.
+    qint64 offset = det < 0 ? -det : det;
+    offset >>= 1;
+
+    qint64 num = a2 * c1 - a1 * c2;
+    *y = ( num < 0 ? num - offset : num + offset ) / det;
+
+    *det_positive = (det > 0);
+
+    return true;
+}
+
+#undef SAME_SIGNS
+
+bool QTessellatorPrivate::Edge::isLeftOf(const Edge &other, Q27Dot5 y) const
+{
+//     QDEBUG() << "isLeftOf" << edge << other.edge << y;
+    qint64 a1 = v1->y - v0->y;
+    qint64 b1 = v0->x - v1->x;
+    qint64 a2 = other.v1->y - other.v0->y;
+    qint64 b2 = other.v0->x - other.v1->x;
+
+    qint64 c2 = qint64(other.v1->x) * other.v0->y - qint64(other.v0->x) * other.v1->y;
+
+    qint64 det = a1 * b2 - a2 * b1;
+    if (det == 0) {
+        // lines are parallel. Only need to check side of one point
+        // fixed ordering for coincident edges
+        qint64 r1 = a2 * v0->x + b2 * v0->y + c2;
+//         QDEBUG() << "det = 0" << r1;
+        if (r1 == 0)
+            return edge < other.edge;
+        return (r1 < 0);
+    }
+
+    // not parallel, need to find the y coordinate of the intersection point
+    qint64 c1 = qint64(v1->x) * v0->y - qint64(v0->x) * v1->y;
+
+    qint64 offset = det < 0 ? -det : det;
+    offset >>= 1;
+
+    qint64 num = a2 * c1 - a1 * c2;
+    qint64 yi = ( num < 0 ? num - offset : num + offset ) / det;
+//     QDEBUG() << "    num=" << num << "offset=" << offset << "det=" << det;
+
+    return ((yi > y) ^ (det < 0));
+}
+
+static inline bool compareVertex(const QTessellatorPrivate::Vertex *p1,
+                                 const QTessellatorPrivate::Vertex *p2)
+{
+    if (p1->y == p2->y) {
+        if (p1->x == p2->x)
+            return p1 < p2;
+        return p1->x < p2->x;
+    }
+    return p1->y < p2->y;
+}
+
+Q27Dot5 QTessellatorPrivate::Edge::positionAt(Q27Dot5 y) const
+{
+    if (y == v0->y)
+        return v0->x;
+    else if (y == v1->y)
+        return v1->x;
+
+    qint64 d = v1->x - v0->x;
+    return (v0->x + d*(y - v0->y)/(v1->y-v0->y));
+}
+
+bool QTessellatorPrivate::EdgeSorter::operator() (const Edge *e1, const Edge *e2)
+{
+    return e1->isLeftOf(*e2, y);
+}
+
+
+QTessellatorPrivate::Scanline::Scanline()
+{
+    edges = 0;
+    edge_table = 0;
+    old = 0;
+}
+
+void QTessellatorPrivate::Scanline::init(int maxActiveEdges)
+{
+    maxActiveEdges *= 2;
+    if (!edges || maxActiveEdges > default_alloc) {
+        max_edges = maxActiveEdges;
+        int s = qMax(maxActiveEdges + 1, default_alloc + 1);
+        edges = q_check_ptr((Edge **)realloc(edges, s*sizeof(Edge *)));
+        edge_table = q_check_ptr((Edge *)realloc(edge_table, s*sizeof(Edge)));
+        old = q_check_ptr((Edge **)realloc(old, s*sizeof(Edge *)));
+    }
+    size = 0;
+    old_size = 0;
+    first_unused = 0;
+    for (int i = 0; i < maxActiveEdges; ++i)
+        edge_table[i].edge = i+1;
+    edge_table[maxActiveEdges].edge = -1;
+}
+
+void QTessellatorPrivate::Scanline::done()
+{
+    if (max_edges > default_alloc) {
+        free(edges);
+        free(old);
+        free(edge_table);
+        edges = 0;
+        old = 0;
+        edge_table = 0;
+    }
+}
+
+QTessellatorPrivate::Scanline::~Scanline()
+{
+    free(edges);
+    free(old);
+    free(edge_table);
+}
+
+int QTessellatorPrivate::Scanline::findEdgePosition(Q27Dot5 x, Q27Dot5 y) const
+{
+    int min = 0;
+    int max = size - 1;
+    while (min < max) {
+        int pos = min + ((max - min + 1) >> 1);
+        Q27Dot5 ax = edges[pos]->positionAt(y);
+        if (ax > x) {
+            max = pos - 1;
+        } else {
+            min = pos;
+        }
+    }
+    return min;
+}
+
+int QTessellatorPrivate::Scanline::findEdgePosition(const Edge &e) const
+{
+//     qDebug() << ">>      findEdgePosition";
+    int min = 0;
+    int max = size;
+    while (min < max) {
+        int pos = min + ((max - min) >> 1);
+//         qDebug() << "        " << min << max << pos << edges[pos]->isLeftOf(e, e.y0);
+        if (edges[pos]->isLeftOf(e, e.v0->y)) {
+            min = pos + 1;
+        } else {
+            max = pos;
+        }
+    }
+//     qDebug() << "<<      findEdgePosition got" << min;
+    return min;
+}
+
+int QTessellatorPrivate::Scanline::findEdge(int edge) const
+{
+    for (int i = 0; i < size; ++i) {
+        int item_edge = edges[i]->edge;
+        if (item_edge == edge)
+            return i;
+    }
+    //Q_ASSERT(false);
+    return -1;
+}
+
+void QTessellatorPrivate::Scanline::clearMarks()
+{
+    for (int i = 0; i < size; ++i) {
+        edges[i]->mark = false;
+        edges[i]->intersect_left = false;
+        edges[i]->intersect_right = false;
+    }
+}
+
+void QTessellatorPrivate::Scanline::prepareLine()
+{
+    Edge **end = edges + size;
+    Edge **e = edges;
+    Edge **o = old;
+    while (e < end) {
+        *o = *e;
+        ++o;
+        ++e;
+    }
+    old_size = size;
+}
+
+void QTessellatorPrivate::Scanline::lineDone()
+{
+    Edge **end = old + old_size;
+    Edge **e = old;
+    while (e < end) {
+        if ((*e)->free) {
+            (*e)->edge = first_unused;
+            first_unused = (*e - edge_table);
+        }
+        ++e;
+    }
+}
+
+void QTessellatorPrivate::Scanline::insert(int pos, const Edge &e)
+{
+    Edge *edge = edge_table + first_unused;
+    first_unused = edge->edge;
+    Q_ASSERT(first_unused != -1);
+    *edge = e;
+    memmove(edges + pos + 1, edges + pos, (size - pos)*sizeof(Edge *));
+    edges[pos] = edge;
+    ++size;
+}
+
+void QTessellatorPrivate::Scanline::removeAt(int pos)
+{
+    Edge *e = edges[pos];
+    e->free = true;
+    --size;
+    memmove(edges + pos, edges + pos + 1, (size - pos)*sizeof(Edge *));
+}
+
+void QTessellatorPrivate::Scanline::markEdges(int pos1, int pos2)
+{
+    if (pos2 < pos1)
+        return;
+
+    for (int i = pos1; i <= pos2; ++i)
+        edges[i]->mark = true;
+}
+
+
+QTessellatorPrivate::Vertices::Vertices()
+{
+    storage = 0;
+    sorted = 0;
+    allocated = 0;
+    nPoints = 0;
+}
+
+QTessellatorPrivate::Vertices::~Vertices()
+{
+    if (storage) {
+        free(storage);
+        free(sorted);
+    }
+}
+
+void QTessellatorPrivate::Vertices::init(int maxVertices)
+{
+    if (!storage || maxVertices > allocated) {
+        int size = qMax((int)default_alloc, maxVertices);
+        storage = q_check_ptr((Vertex *)realloc(storage, size*sizeof(Vertex)));
+        sorted = q_check_ptr((Vertex **)realloc(sorted, size*sizeof(Vertex *)));
+        allocated = maxVertices;
+    }
+}
+
+void QTessellatorPrivate::Vertices::done()
+{
+    if (allocated > default_alloc) {
+        free(storage);
+        free(sorted);
+        storage = 0;
+        sorted = 0;
+        allocated = 0;
+    }
+}
+
+
+
+static inline void fillTrapezoid(Q27Dot5 y1, Q27Dot5 y2, int left, int right,
+                                 const QTessellatorPrivate::Vertices &vertices,
+                                 QTessellator::Trapezoid *trap)
+{
+    trap->top = y1;
+    trap->bottom = y2;
+    const QTessellatorPrivate::Vertex *v = vertices[left];
+    trap->topLeft = v;
+    trap->bottomLeft = vertices.next(v);
+    if (trap->topLeft->y > trap->bottomLeft->y)
+        qSwap(trap->topLeft,trap->bottomLeft);
+    v = vertices[right];
+    trap->topRight = v;
+    trap->bottomRight = vertices.next(v);
+    if (trap->topRight->y > trap->bottomRight->y)
+        qSwap(trap->topRight, trap->bottomRight);
+}
+
+QRectF QTessellatorPrivate::collectAndSortVertices(const QPointF *points, int *maxActiveEdges)
+{
+    *maxActiveEdges = 0;
+    Vertex *v = vertices.storage;
+    Vertex **vv = vertices.sorted;
+
+    qreal xmin(points[0].x());
+    qreal xmax(points[0].x());
+    qreal ymin(points[0].y());
+    qreal ymax(points[0].y());
+
+    // collect vertex data
+    Q27Dot5 y_prev = FloatToQ27Dot5(points[vertices.nPoints-1].y());
+    Q27Dot5 x_next = FloatToQ27Dot5(points[0].x());
+    Q27Dot5 y_next = FloatToQ27Dot5(points[0].y());
+    int j = 0;
+    int i = 0;
+    while (i < vertices.nPoints) {
+        Q27Dot5 y_curr = y_next;
+
+        *vv = v;
+
+        v->x = x_next;
+        v->y = y_next;
+        v->flags = 0;
+
+    next_point:
+
+        xmin = qMin(xmin, points[i+1].x());
+        xmax = qMax(xmax, points[i+1].x());
+        ymin = qMin(ymin, points[i+1].y());
+        ymax = qMax(ymax, points[i+1].y());
+
+        y_next = FloatToQ27Dot5(points[i+1].y());
+        x_next = FloatToQ27Dot5(points[i+1].x());
+
+        // skip vertices on top of each other
+        if (v->x == x_next && v->y == y_next) {
+            ++i;
+            if (i < vertices.nPoints)
+                goto next_point;
+            Vertex *v0 = vertices.storage;
+            v0->flags &= ~(LineBeforeStarts|LineBeforeEnds|LineBeforeHorizontal);
+            if (y_prev < y_curr)
+                v0->flags |= LineBeforeEnds;
+            else if (y_prev > y_curr)
+                v0->flags |= LineBeforeStarts;
+            else
+                v0->flags |= LineBeforeHorizontal;
+            if ((v0->flags & (LineBeforeStarts|LineAfterStarts))
+                && !(v0->flags & (LineAfterEnds|LineBeforeEnds)))
+                *maxActiveEdges += 2;
+            break;
+        }
+
+        if (y_prev < y_curr)
+            v->flags |= LineBeforeEnds;
+        else if (y_prev > y_curr)
+            v->flags |= LineBeforeStarts;
+        else
+            v->flags |= LineBeforeHorizontal;
+
+
+        if (y_curr < y_next)
+            v->flags |= LineAfterStarts;
+        else if (y_curr > y_next)
+            v->flags |= LineAfterEnds;
+        else
+            v->flags |= LineAfterHorizontal;
+        // ### could probably get better limit by looping over sorted list and counting down on ending edges
+        if ((v->flags & (LineBeforeStarts|LineAfterStarts))
+            && !(v->flags & (LineAfterEnds|LineBeforeEnds)))
+            *maxActiveEdges += 2;
+        y_prev = y_curr;
+        ++v;
+        ++vv;
+        ++j;
+        ++i;
+    }
+    vertices.nPoints = j;
+
+    QDEBUG() << "maxActiveEdges=" << *maxActiveEdges;
+    vv = vertices.sorted;
+    qSort(vv, vv + vertices.nPoints, compareVertex);
+
+    return QRectF(xmin, ymin, xmax-xmin, ymax-ymin);
+}
+
+struct QCoincidingEdge {
+    QTessellatorPrivate::Vertex *start;
+    QTessellatorPrivate::Vertex *end;
+    bool used;
+    bool before;
+
+    inline bool operator<(const QCoincidingEdge &e2) const
+    {
+        return end->y == e2.end->y ? end->x < e2.end->x : end->y < e2.end->y;
+    }
+};
+
+static void cancelEdges(QCoincidingEdge &e1, QCoincidingEdge &e2)
+{
+    if (e1.before) {
+        e1.start->flags &= ~(LineBeforeStarts|LineBeforeHorizontal);
+        e1.end->flags &= ~(LineAfterEnds|LineAfterHorizontal);
+    } else {
+        e1.start->flags &= ~(LineAfterStarts|LineAfterHorizontal);
+        e1.end->flags &= ~(LineBeforeEnds|LineBeforeHorizontal);
+    }
+    if (e2.before) {
+        e2.start->flags &= ~(LineBeforeStarts|LineBeforeHorizontal);
+        e2.end->flags &= ~(LineAfterEnds|LineAfterHorizontal);
+    } else {
+        e2.start->flags &= ~(LineAfterStarts|LineAfterHorizontal);
+        e2.end->flags &= ~(LineBeforeEnds|LineBeforeHorizontal);
+    }
+    e1.used = e2.used = true;
+}
+
+void QTessellatorPrivate::cancelCoincidingEdges()
+{
+    Vertex **vv = vertices.sorted;
+
+    QCoincidingEdge *tl = 0;
+    int tlSize = 0;
+
+    for (int i = 0; i < vertices.nPoints - 1; ++i) {
+        Vertex *v = vv[i];
+        int testListSize = 0;
+        while (i < vertices.nPoints - 1) {
+            Vertex *n = vv[i];
+            if (v->x != n->x || v->y != n->y)
+                break;
+
+            if (testListSize > tlSize - 2) {
+                tlSize = qMax(tlSize*2, 16);
+                tl = q_check_ptr((QCoincidingEdge *)realloc(tl, tlSize*sizeof(QCoincidingEdge)));
+            }
+            if (n->flags & (LineBeforeStarts|LineBeforeHorizontal)) {
+                tl[testListSize].start = n;
+                tl[testListSize].end = vertices.prev(n);
+                tl[testListSize].used = false;
+                tl[testListSize].before = true;
+                ++testListSize;
+            }
+            if (n->flags & (LineAfterStarts|LineAfterHorizontal)) {
+                tl[testListSize].start = n;
+                tl[testListSize].end = vertices.next(n);
+                tl[testListSize].used = false;
+                tl[testListSize].before = false;
+                ++testListSize;
+            }
+            ++i;
+        }
+        if (!testListSize)
+            continue;
+
+        qSort(tl, tl + testListSize);
+
+        for (int j = 0; j < testListSize; ++j) {
+            if (tl[j].used)
+                continue;
+
+            for (int k = j + 1; k < testListSize; ++k) {
+                if (tl[j].end->x != tl[k].end->x
+                    || tl[j].end->y != tl[k].end->y
+                    || tl[k].used)
+                    break;
+
+                if (!winding || tl[j].before != tl[k].before) {
+                    cancelEdges(tl[j], tl[k]);
+                    break;
+                }
+                ++k;
+            }
+            ++j;
+        }
+    }
+    free(tl);
+}
+
+
+void QTessellatorPrivate::emitEdges(QTessellator *tessellator)
+{
+    //QDEBUG() << "TRAPS:";
+    if (!scanline.old_size)
+        return;
+
+    // emit edges
+    if (winding) {
+        // winding fill rule
+        int w = 0;
+
+        scanline.old[0]->y_left = y;
+
+        for (int i = 0; i < scanline.old_size - 1; ++i) {
+            Edge *left = scanline.old[i];
+            Edge *right = scanline.old[i+1];
+            w += left->winding;
+//             qDebug() << "i=" << i << "edge->winding=" << left->winding << "winding=" << winding;
+            if (w == 0) {
+                left->y_right = y;
+                right->y_left = y;
+            } else if (!emit_clever || left->mark || right->mark) {
+                Q27Dot5 top = qMax(left->y_right, right->y_left);
+                if (top != y) {
+                    QTessellator::Trapezoid trap;
+                    fillTrapezoid(top, y, left->edge, right->edge, vertices, &trap);
+                    tessellator->addTrap(trap);
+//                     QDEBUG() << "    top=" << Q27Dot5ToDouble(top) << "left=" << left->edge << "right=" << right->edge;
+                }
+                right->y_left = y;
+                left->y_right = y;
+            }
+            left->mark = false;
+        }
+        if (scanline.old[scanline.old_size - 1]->mark) {
+            scanline.old[scanline.old_size - 1]->y_right = y;
+            scanline.old[scanline.old_size - 1]->mark = false;
+        }
+    } else {
+        // odd-even fill rule
+        for (int i = 0; i < scanline.old_size; i += 2) {
+            Edge *left = scanline.old[i];
+            Edge *right = scanline.old[i+1];
+            if (!emit_clever || left->mark || right->mark) {
+                Q27Dot5 top = qMax(left->y_right, right->y_left);
+                if (top != y) {
+                    QTessellator::Trapezoid trap;
+                    fillTrapezoid(top, y, left->edge, right->edge, vertices, &trap);
+                    tessellator->addTrap(trap);
+                }
+//                 QDEBUG() << "    top=" << Q27Dot5ToDouble(top) << "left=" << left->edge << "right=" << right->edge;
+                left->y_left = y;
+                left->y_right = y;
+                right->y_left = y;
+                right->y_right = y;
+                left->mark = right->mark = false;
+            }
+        }
+    }
+}
+
+
+void QTessellatorPrivate::processIntersections()
+{
+    QDEBUG() << "PROCESS INTERSECTIONS";
+    // process intersections
+    while (!intersections.isEmpty()) {
+        Intersections::iterator it = intersections.begin();
+        if (it.key().y != y)
+            break;
+
+        // swap edges
+        QDEBUG() << "    swapping intersecting edges ";
+        int min = scanline.size;
+        int max = 0;
+        Q27Dot5 xmin = INT_MAX;
+        Q27Dot5 xmax = INT_MIN;
+        int num = 0;
+        while (1) {
+            const Intersection &i = it.key();
+            int next = it->next;
+
+            int edgePos = scanline.findEdge(i.edge);
+            if (edgePos >= 0) {
+                ++num;
+                min = qMin(edgePos, min);
+                max = qMax(edgePos, max);
+                Edge *edge = scanline.edges[edgePos];
+                xmin = qMin(xmin, edge->positionAt(y));
+                xmax = qMax(xmax, edge->positionAt(y));
+            }
+            Intersection key;
+            key.y = y;
+            key.edge = next;
+            it = intersections.find(key);
+            intersections.remove(i);
+            if (it == intersections.end())
+                break;
+        }
+        if (num < 2)
+            continue;
+
+        Q_ASSERT(min != max);
+        QDEBUG() << "sorting between" << min << "and" << max << "xpos=" << xmin << xmax;
+        while (min > 0 && scanline.edges[min - 1]->positionAt(y) >= xmin) {
+            QDEBUG() << "    adding edge on left";
+            --min;
+        }
+        while (max < scanline.size - 1 && scanline.edges[max + 1]->positionAt(y) <=  xmax) {
+            QDEBUG() << "    adding edge on right";
+            ++max;
+        }
+
+        qSort(scanline.edges + min, scanline.edges + max + 1, EdgeSorter(y));
+#ifdef DEBUG
+        for (int i = min; i <= max; ++i)
+            QDEBUG() << "        " << scanline.edges[i]->edge << "at pos" << i;
+#endif
+        for (int i = min; i <= max; ++i) {
+            Edge *edge = scanline.edges[i];
+            edge->intersect_left = true;
+            edge->intersect_right = true;
+            edge->mark = true;
+        }
+    }
+}
+
+void QTessellatorPrivate::removeEdges()
+{
+    int cv = currentVertex;
+    while (cv < vertices.nPoints) {
+        const Vertex *v = vertices.sorted[cv];
+        if (v->y > y)
+            break;
+        if (v->flags & LineBeforeEnds) {
+            QDEBUG() << "    removing edge" << vertices.prevPos(v);
+            int pos = scanline.findEdge(vertices.prevPos(v));
+            if (pos == -1)
+                continue;
+            scanline.edges[pos]->mark = true;
+            if (pos > 0)
+                scanline.edges[pos - 1]->intersect_right = true;
+            if (pos < scanline.size - 1)
+                scanline.edges[pos + 1]->intersect_left = true;
+            scanline.removeAt(pos);
+        }
+        if (v->flags & LineAfterEnds) {
+            QDEBUG() << "    removing edge" << vertices.position(v);
+            int pos = scanline.findEdge(vertices.position(v));
+            if (pos == -1)
+                continue;
+            scanline.edges[pos]->mark = true;
+            if (pos > 0)
+                scanline.edges[pos - 1]->intersect_right = true;
+            if (pos < scanline.size - 1)
+                scanline.edges[pos + 1]->intersect_left = true;
+            scanline.removeAt(pos);
+        }
+        ++cv;
+    }
+}
+
+void QTessellatorPrivate::addEdges()
+{
+    while (currentVertex < vertices.nPoints) {
+        const Vertex *v = vertices.sorted[currentVertex];
+        if (v->y > y)
+            break;
+        if (v->flags & LineBeforeStarts) {
+            // add new edge
+            int start = vertices.prevPos(v);
+            Edge e(vertices, start);
+            int pos = scanline.findEdgePosition(e);
+            QDEBUG() << "    adding edge" << start << "at position" << pos;
+            scanline.insert(pos, e);
+            if (!mark_clever || !(v->flags & LineAfterEnds)) {
+                if (pos > 0)
+                    scanline.edges[pos - 1]->mark = true;
+                if (pos < scanline.size - 1)
+                    scanline.edges[pos + 1]->mark = true;
+            }
+        }
+        if (v->flags & LineAfterStarts) {
+            Edge e(vertices, vertices.position(v));
+            int pos = scanline.findEdgePosition(e);
+            QDEBUG() << "    adding edge" << vertices.position(v) << "at position" << pos;
+            scanline.insert(pos, e);
+            if (!mark_clever || !(v->flags & LineBeforeEnds)) {
+                if (pos > 0)
+                    scanline.edges[pos - 1]->mark = true;
+                if (pos < scanline.size - 1)
+                    scanline.edges[pos + 1]->mark = true;
+            }
+        }
+        if (v->flags & LineAfterHorizontal) {
+            int pos1 = scanline.findEdgePosition(v->x, v->y);
+            const Vertex *next = vertices.next(v);
+            Q_ASSERT(v->y == next->y);
+            int pos2 = scanline.findEdgePosition(next->x, next->y);
+            if (pos2 < pos1)
+                qSwap(pos1, pos2);
+            if (pos1 > 0)
+                --pos1;
+            if (pos2 == scanline.size)
+                --pos2;
+            //QDEBUG() << "marking horizontal edge from " << pos1 << "to" << pos2;
+            scanline.markEdges(pos1, pos2);
+        }
+        ++currentVertex;
+    }
+}
+
+#ifdef DEBUG
+static void checkLinkChain(const QTessellatorPrivate::Intersections &intersections,
+                           QTessellatorPrivate::Intersection i)
+{
+//     qDebug() << "              Link chain: ";
+    int end = i.edge;
+    while (1) {
+        QTessellatorPrivate::IntersectionLink l = intersections.value(i);
+//         qDebug() << "                     " << i.edge << "next=" << l.next << "prev=" << l.prev;
+        if (l.next == end)
+            break;
+        Q_ASSERT(l.next != -1);
+        Q_ASSERT(l.prev != -1);
+
+        QTessellatorPrivate::Intersection i2 = i;
+        i2.edge = l.next;
+        QTessellatorPrivate::IntersectionLink l2 = intersections.value(i2);
+
+        Q_ASSERT(l2.next != -1);
+        Q_ASSERT(l2.prev != -1);
+        Q_ASSERT(l.next == i2.edge);
+        Q_ASSERT(l2.prev == i.edge);
+        i = i2;
+    }
+}
+#endif
+
+bool QTessellatorPrivate::edgeInChain(Intersection i, int edge)
+{
+    int end = i.edge;
+    while (1) {
+        if (i.edge == edge)
+            return true;
+        IntersectionLink l = intersections.value(i);
+        if (l.next == end)
+            break;
+        Q_ASSERT(l.next != -1);
+        Q_ASSERT(l.prev != -1);
+
+        Intersection i2 = i;
+        i2.edge = l.next;
+
+#ifndef QT_NO_DEBUG
+        IntersectionLink l2 = intersections.value(i2);
+        Q_ASSERT(l2.next != -1);
+        Q_ASSERT(l2.prev != -1);
+        Q_ASSERT(l.next == i2.edge);
+        Q_ASSERT(l2.prev == i.edge);
+#endif
+        i = i2;
+    }
+    return false;
+}
+
+
+void QTessellatorPrivate::addIntersection(const Edge *e1, const Edge *e2)
+{
+    const IntersectionLink emptyLink = {-1, -1};
+
+    int next = vertices.nextPos(vertices[e1->edge]);
+    if (e2->edge == next)
+        return;
+    int prev = vertices.prevPos(vertices[e1->edge]);
+    if (e2->edge == prev)
+        return;
+
+    Q27Dot5 yi;
+    bool det_positive;
+    bool isect = e1->intersect(*e2, &yi, &det_positive);
+    QDEBUG("checking edges %d and %d", e1->edge, e2->edge);
+    if (!isect) {
+        QDEBUG() << "    no intersection";
+        return;
+    }
+
+    // don't emit an intersection if it's at the start of a line segment or above us
+    if (yi <= y) {
+        if (!det_positive)
+            return;
+        QDEBUG() << "        ----->>>>>> WRONG ORDER!";
+        yi = y;
+    }
+    QDEBUG() << "   between edges " << e1->edge << "and" << e2->edge << "at point ("
+             << Q27Dot5ToDouble(yi) << ')';
+
+    Intersection i1;
+    i1.y = yi;
+    i1.edge = e1->edge;
+    IntersectionLink link1 = intersections.value(i1, emptyLink);
+    Intersection i2;
+    i2.y = yi;
+    i2.edge = e2->edge;
+    IntersectionLink link2 = intersections.value(i2, emptyLink);
+
+    // new pair of edges
+    if (link1.next == -1 && link2.next == -1) {
+        link1.next = link1.prev = i2.edge;
+        link2.next = link2.prev = i1.edge;
+    } else if (link1.next == i2.edge || link1.prev == i2.edge
+               || link2.next == i1.edge || link2.prev == i1.edge) {
+#ifdef DEBUG
+        checkLinkChain(intersections, i1);
+        checkLinkChain(intersections, i2);
+        Q_ASSERT(edgeInChain(i1, i2.edge));
+#endif
+        return;
+    } else if (link1.next == -1 || link2.next == -1) {
+        if (link2.next == -1) {
+            qSwap(i1, i2);
+            qSwap(link1, link2);
+        }
+        Q_ASSERT(link1.next == -1);
+#ifdef DEBUG
+        checkLinkChain(intersections, i2);
+#endif
+        // only i2 in list
+        link1.next = i2.edge;
+        link1.prev = link2.prev;
+        link2.prev = i1.edge;
+        Intersection other;
+        other.y = yi;
+        other.edge = link1.prev;
+        IntersectionLink link = intersections.value(other, emptyLink);
+        Q_ASSERT(link.next == i2.edge);
+        Q_ASSERT(link.prev != -1);
+        link.next = i1.edge;
+        intersections.insert(other, link);
+    } else {
+        bool connected = edgeInChain(i1, i2.edge);
+        if (connected)
+            return;
+#ifdef DEBUG
+        checkLinkChain(intersections, i1);
+        checkLinkChain(intersections, i2);
+#endif
+        // both already in some list. Have to make sure they are connected
+        // this can be done by cutting open the ring(s) after the two eges and
+        // connecting them again
+        Intersection other1;
+        other1.y = yi;
+        other1.edge = link1.next;
+        IntersectionLink linko1 = intersections.value(other1, emptyLink);
+        Intersection other2;
+        other2.y = yi;
+        other2.edge = link2.next;
+        IntersectionLink linko2 = intersections.value(other2, emptyLink);
+
+        linko1.prev = i2.edge;
+        link2.next = other1.edge;
+
+        linko2.prev = i1.edge;
+        link1.next = other2.edge;
+        intersections.insert(other1, linko1);
+        intersections.insert(other2, linko2);
+    }
+    intersections.insert(i1, link1);
+    intersections.insert(i2, link2);
+#ifdef DEBUG
+    checkLinkChain(intersections, i1);
+    checkLinkChain(intersections, i2);
+    Q_ASSERT(edgeInChain(i1, i2.edge));
+#endif
+    return;
+
+}
+
+
+void QTessellatorPrivate::addIntersections()
+{
+    if (scanline.size) {
+        QDEBUG() << "INTERSECTIONS";
+        // check marked edges for intersections
+#ifdef DEBUG
+        for (int i = 0; i < scanline.size; ++i) {
+            Edge *e = scanline.edges[i];
+            QDEBUG() << "    " << i << e->edge << "isect=(" << e->intersect_left << e->intersect_right
+                     << ')';
+        }
+#endif
+
+        for (int i = 0; i < scanline.size - 1; ++i) {
+            Edge *e1 = scanline.edges[i];
+            Edge *e2 = scanline.edges[i + 1];
+            // check for intersection
+            if (e1->intersect_right || e2->intersect_left)
+                addIntersection(e1, e2);
+        }
+    }
+#if 0
+    if (intersections.constBegin().key().y == y) {
+        QDEBUG() << "----------------> intersection on same line";
+        scanline.clearMarks();
+        scanline.processIntersections(y, &intersections);
+        goto redo;
+    }
+#endif
+}
+
+
+QTessellator::QTessellator()
+{
+    d = new QTessellatorPrivate;
+}
+
+QTessellator::~QTessellator()
+{
+    delete d;
+}
+
+void QTessellator::setWinding(bool w)
+{
+    d->winding = w;
+}
+
+
+QRectF QTessellator::tessellate(const QPointF *points, int nPoints)
+{
+    Q_ASSERT(points[0] == points[nPoints-1]);
+    --nPoints;
+
+#ifdef DEBUG
+    QDEBUG()<< "POINTS:";
+    for (int i = 0; i < nPoints; ++i) {
+        QDEBUG() << points[i];
+    }
+#endif
+
+    // collect edges and calculate bounds
+    d->vertices.nPoints = nPoints;
+    d->vertices.init(nPoints);
+
+    int maxActiveEdges = 0;
+    QRectF br = d->collectAndSortVertices(points, &maxActiveEdges);
+    d->cancelCoincidingEdges();
+
+#ifdef DEBUG
+    QDEBUG() << "nPoints = " << nPoints << "using " << d->vertices.nPoints;
+    QDEBUG()<< "VERTICES:";
+    for (int i = 0; i < d->vertices.nPoints; ++i) {
+        QDEBUG() << "    " << i << ": "
+                 << "point=" << d->vertices.position(d->vertices.sorted[i])
+                 << "flags=" << d->vertices.sorted[i]->flags
+                 << "pos=(" << Q27Dot5ToDouble(d->vertices.sorted[i]->x) << '/'
+                 << Q27Dot5ToDouble(d->vertices.sorted[i]->y) << ')';
+    }
+#endif
+
+    d->scanline.init(maxActiveEdges);
+    d->y = INT_MIN/256;
+    d->currentVertex = 0;
+
+    while (d->currentVertex < d->vertices.nPoints) {
+        d->scanline.clearMarks();
+
+        d->y = d->vertices.sorted[d->currentVertex]->y;
+        if (!d->intersections.isEmpty())
+            d->y = qMin(d->y, d->intersections.constBegin().key().y);
+
+        QDEBUG()<< "===== SCANLINE: y =" << Q27Dot5ToDouble(d->y) << " =====";
+
+        d->scanline.prepareLine();
+        d->processIntersections();
+        d->removeEdges();
+        d->addEdges();
+        d->addIntersections();
+        d->emitEdges(this);
+        d->scanline.lineDone();
+
+#ifdef DEBUG
+        QDEBUG()<< "===== edges:";
+        for (int i = 0; i < d->scanline.size; ++i) {
+            QDEBUG() << "   " << d->scanline.edges[i]->edge
+                     << "p0= (" << Q27Dot5ToDouble(d->scanline.edges[i]->v0->x)
+                     << '/' << Q27Dot5ToDouble(d->scanline.edges[i]->v0->y)
+                     << ") p1= (" << Q27Dot5ToDouble(d->scanline.edges[i]->v1->x)
+                     << '/' << Q27Dot5ToDouble(d->scanline.edges[i]->v1->y) << ')'
+                     << "x=" << Q27Dot5ToDouble(d->scanline.edges[i]->positionAt(d->y))
+                     << "isLeftOfNext="
+                     << ((i < d->scanline.size - 1)
+                         ? d->scanline.edges[i]->isLeftOf(*d->scanline.edges[i+1], d->y)
+                         : true);
+        }
+#endif
+}
+
+    d->scanline.done();
+    d->intersections.clear();
+    return br;
+}
+
+// tessellates the given convex polygon
+void QTessellator::tessellateConvex(const QPointF *points, int nPoints)
+{
+    Q_ASSERT(points[0] == points[nPoints-1]);
+    --nPoints;
+
+    d->vertices.nPoints = nPoints;
+    d->vertices.init(nPoints);
+
+    for (int i = 0; i < nPoints; ++i) {
+        d->vertices[i]->x = FloatToQ27Dot5(points[i].x());
+        d->vertices[i]->y = FloatToQ27Dot5(points[i].y());
+    }
+
+    int left = 0, right = 0;
+
+    int top = 0;
+    for (int i = 1; i < nPoints; ++i) {
+        if (d->vertices[i]->y < d->vertices[top]->y)
+            top = i;
+    }
+
+    left = (top + nPoints - 1) % nPoints;
+    right = (top + 1) % nPoints;
+
+    while (d->vertices[left]->x == d->vertices[top]->x && d->vertices[left]->y == d->vertices[top]->y && left != right)
+        left = (left + nPoints - 1) % nPoints;
+
+    while (d->vertices[right]->x == d->vertices[top]->x && d->vertices[right]->y == d->vertices[top]->y && left != right)
+        right = (right + 1) % nPoints;
+
+    if (left == right)
+        return;
+
+    int dir = 1;
+
+    Vertex dLeft = { d->vertices[top]->x - d->vertices[left]->x,
+                     d->vertices[top]->y - d->vertices[left]->y };
+
+    Vertex dRight = { d->vertices[right]->x - d->vertices[top]->x,
+                      d->vertices[right]->y - d->vertices[top]->y };
+
+    Q27Dot5 cross = dLeft.x * dRight.y - dLeft.y * dRight.x;
+
+    // flip direction if polygon is clockwise
+    if (cross < 0 || (cross == 0 && dLeft.x > 0)) {
+        qSwap(left, right);
+        dir = -1;
+    }
+
+    Vertex *lastLeft = d->vertices[top];
+    Vertex *lastRight = d->vertices[top];
+
+    QTessellator::Trapezoid trap;
+
+    while (lastLeft->y == d->vertices[left]->y && left != right) {
+        lastLeft = d->vertices[left];
+        left = (left + nPoints - dir) % nPoints;
+    }
+
+    while (lastRight->y == d->vertices[right]->y && left != right) {
+        lastRight = d->vertices[right];
+        right = (right + nPoints + dir) % nPoints;
+    }
+
+    while (true) {
+        trap.top = qMax(lastRight->y, lastLeft->y);
+        trap.bottom = qMin(d->vertices[left]->y, d->vertices[right]->y);
+        trap.topLeft = lastLeft;
+        trap.topRight = lastRight;
+        trap.bottomLeft = d->vertices[left];
+        trap.bottomRight = d->vertices[right];
+
+        if (trap.bottom > trap.top)
+            addTrap(trap);
+
+        if (left == right)
+            break;
+
+        if (d->vertices[right]->y < d->vertices[left]->y) {
+            do {
+                lastRight = d->vertices[right];
+                right = (right + nPoints + dir) % nPoints;
+            }
+            while (lastRight->y == d->vertices[right]->y && left != right);
+        } else {
+            do {
+                lastLeft = d->vertices[left];
+                left = (left + nPoints - dir) % nPoints;
+            }
+            while (lastLeft->y == d->vertices[left]->y && left != right);
+        }
+    }
+}
+
+// tessellates the stroke of the line from a_ to b_ with the given width and a flat cap
+void QTessellator::tessellateRect(const QPointF &a_, const QPointF &b_, qreal width)
+{
+    Vertex a = { FloatToQ27Dot5(a_.x()), FloatToQ27Dot5(a_.y()) };
+    Vertex b = { FloatToQ27Dot5(b_.x()), FloatToQ27Dot5(b_.y()) };
+
+    QPointF pa = a_, pb = b_;
+
+    if (a.y > b.y) {
+        qSwap(a, b);
+        qSwap(pa, pb);
+    }
+
+    Vertex delta = { b.x - a.x, b.y - a.y };
+
+    if (delta.x == 0 && delta.y == 0)
+        return;
+
+    qreal hw = 0.5 * width;
+
+    if (delta.x == 0) {
+        Q27Dot5 halfWidth = FloatToQ27Dot5(hw);
+
+        if (halfWidth == 0)
+            return;
+
+        Vertex topLeft = { a.x - halfWidth, a.y };
+        Vertex topRight = { a.x + halfWidth, a.y };
+        Vertex bottomLeft = { a.x - halfWidth, b.y };
+        Vertex bottomRight = { a.x + halfWidth, b.y };
+
+        QTessellator::Trapezoid trap = { topLeft.y, bottomLeft.y, &topLeft, &bottomLeft, &topRight, &bottomRight };
+        addTrap(trap);
+    } else if (delta.y == 0) {
+        Q27Dot5 halfWidth = FloatToQ27Dot5(hw);
+
+        if (halfWidth == 0)
+            return;
+
+        if (a.x > b.x)
+            qSwap(a.x, b.x);
+
+        Vertex topLeft = { a.x, a.y - halfWidth };
+        Vertex topRight = { b.x, a.y - halfWidth };
+        Vertex bottomLeft = { a.x, a.y + halfWidth };
+        Vertex bottomRight = { b.x, a.y + halfWidth };
+
+        QTessellator::Trapezoid trap = { topLeft.y, bottomLeft.y, &topLeft, &bottomLeft, &topRight, &bottomRight };
+        addTrap(trap);
+    } else {
+        QPointF perp(pb.y() - pa.y(), pa.x() - pb.x());
+        qreal length = qSqrt(perp.x() * perp.x() + perp.y() * perp.y());
+
+        if (qFuzzyIsNull(length))
+            return;
+
+        // need the half of the width
+        perp *= hw / length;
+
+        QPointF pta = pa + perp;
+        QPointF ptb = pa - perp;
+        QPointF ptc = pb - perp;
+        QPointF ptd = pb + perp;
+
+        Vertex ta = { FloatToQ27Dot5(pta.x()), FloatToQ27Dot5(pta.y()) };
+        Vertex tb = { FloatToQ27Dot5(ptb.x()), FloatToQ27Dot5(ptb.y()) };
+        Vertex tc = { FloatToQ27Dot5(ptc.x()), FloatToQ27Dot5(ptc.y()) };
+        Vertex td = { FloatToQ27Dot5(ptd.x()), FloatToQ27Dot5(ptd.y()) };
+
+        if (ta.y < tb.y) {
+            if (tb.y < td.y) {
+                QTessellator::Trapezoid top = { ta.y, tb.y, &ta, &tb, &ta, &td };
+                QTessellator::Trapezoid bottom = { td.y, tc.y, &tb, &tc, &td, &tc };
+                addTrap(top);
+                addTrap(bottom);
+
+                QTessellator::Trapezoid middle = { tb.y, td.y, &tb, &tc, &ta, &td };
+                addTrap(middle);
+            } else {
+                QTessellator::Trapezoid top = { ta.y, td.y, &ta, &tb, &ta, &td };
+                QTessellator::Trapezoid bottom = { tb.y, tc.y, &tb, &tc, &td, &tc };
+                addTrap(top);
+                addTrap(bottom);
+
+                if (tb.y != td.y) {
+                    QTessellator::Trapezoid middle = { td.y, tb.y, &ta, &tb, &td, &tc };
+                    addTrap(middle);
+                }
+            }
+        } else {
+            if (ta.y < tc.y) {
+                QTessellator::Trapezoid top = { tb.y, ta.y, &tb, &tc, &tb, &ta };
+                QTessellator::Trapezoid bottom = { tc.y, td.y, &tc, &td, &ta, &td };
+                addTrap(top);
+                addTrap(bottom);
+
+                QTessellator::Trapezoid middle = { ta.y, tc.y, &tb, &tc, &ta, &td };
+                addTrap(middle);
+            } else {
+                QTessellator::Trapezoid top = { tb.y, tc.y, &tb, &tc, &tb, &ta };
+                QTessellator::Trapezoid bottom = { ta.y, td.y, &tc, &td, &ta, &td };
+                addTrap(top);
+                addTrap(bottom);
+
+                if (ta.y != tc.y) {
+                    QTessellator::Trapezoid middle = { tc.y, ta.y, &tc, &td, &tb, &ta };
+                    addTrap(middle);
+                }
+            }
+        }
+    }
+}
+
+QT_END_NAMESPACE