Exemplo n.º 1
0
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);
}
Exemplo n.º 2
0
// 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);
        }
    }
}
Exemplo n.º 3
0
// 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);
                }
            }
        }
    }
}
Exemplo n.º 4
0
void old_tesselate_polygon(QVector<XTrapezoid> *traps, const QPointF *pg, int pgSize,
                           bool winding)
{
    QVector<QEdge> edges;
    edges.reserve(128);
    qreal ymin(INT_MAX/256);
    qreal ymax(INT_MIN/256);

    //painter.begin(pg, pgSize);
    if (pg[0] != pg[pgSize-1])
        qWarning() << Q_FUNC_INFO << "Malformed polygon (first and last points must be identical)";
    // generate edge table
//     qDebug() << "POINTS:";
    for (int x = 0; x < pgSize-1; ++x) {
	QEdge edge;
        QPointF p1(Q27Dot5ToDouble(FloatToQ27Dot5(pg[x].x())),
                   Q27Dot5ToDouble(FloatToQ27Dot5(pg[x].y())));
        QPointF p2(Q27Dot5ToDouble(FloatToQ27Dot5(pg[x+1].x())),
                   Q27Dot5ToDouble(FloatToQ27Dot5(pg[x+1].y())));

//         qDebug() << "    "
//                  << p1;
	edge.winding = p1.y() > p2.y() ? 1 : -1;
	if (edge.winding > 0)
            qSwap(p1, p2);
        edge.p1.x = XDoubleToFixed(p1.x());
        edge.p1.y = XDoubleToFixed(p1.y());
        edge.p2.x = XDoubleToFixed(p2.x());
        edge.p2.y = XDoubleToFixed(p2.y());

	edge.m = (p1.y() - p2.y()) / (p1.x() - p2.x()); // line derivative
	edge.b = p1.y() - edge.m * p1.x(); // intersection with y axis
	edge.m = edge.m != 0.0 ? 1.0 / edge.m : 0.0; // inverted derivative
	edges.append(edge);
        ymin = qMin(ymin, qreal(XFixedToDouble(edge.p1.y)));
        ymax = qMax(ymax, qreal(XFixedToDouble(edge.p2.y)));
    }

    QList<const QEdge *> et; 	    // edge list
    for (int i = 0; i < edges.size(); ++i)
        et.append(&edges.at(i));

    // sort edge table by min y value
    qSort(et.begin(), et.end(), compareEdges);

    // eliminate shared edges
    for (int i = 0; i < et.size(); ++i) {
	for (int k = i+1; k < et.size(); ++k) {
            const QEdge *edgeI = et.at(i);
            const QEdge *edgeK = et.at(k);
            if (edgeK->p1.y > edgeI->p1.y)
                break;
   	    if (edgeI->winding != edgeK->winding &&
                isEqual(edgeI->p1, edgeK->p1) && isEqual(edgeI->p2, edgeK->p2)
		) {
 		et.removeAt(k);
		et.removeAt(i);
		--i;
		break;
	    }
	}
    }

    if (ymax <= ymin)
	return;
    QList<const QEdge *> aet; 	    // edges that intersects the current scanline

//     if (ymin < 0)
// 	ymin = 0;
//     if (paintEventClipRegion) // don't scan more lines than we have to
// 	ymax = paintEventClipRegion->boundingRect().height();

#ifdef QT_DEBUG_TESSELATOR
    qDebug("==> ymin = %f, ymax = %f", ymin, ymax);
#endif // QT_DEBUG_TESSELATOR

    currentY = ymin; // used by the less than op
    for (qreal y = ymin; y < ymax;) {
	// fill active edge table with edges that intersect the current line
	for (int i = 0; i < et.size(); ++i) {
            const QEdge *edge = et.at(i);
            if (edge->p1.y > XDoubleToFixed(y))
                break;
            aet.append(edge);
            et.removeAt(i);
            --i;
	}

	// remove processed edges from active edge table
	for (int i = 0; i < aet.size(); ++i) {
	    if (aet.at(i)->p2.y <= XDoubleToFixed(y)) {
		aet.removeAt(i);
 		--i;
	    }
	}
        if (aet.size()%2 != 0) {
#ifndef QT_NO_DEBUG
            qWarning("QX11PaintEngine: aet out of sync - this should not happen.");
#endif
            return;
        }

	// done?
	if (!aet.size()) {
            if (!et.size()) {
                break;
	    } else {
 		y = currentY = XFixedToDouble(et.at(0)->p1.y);
                continue;
	    }
        }

        // calculate the next y where we have to start a new set of trapezoids
	qreal next_y(INT_MAX/256);
 	for (int i = 0; i < aet.size(); ++i) {
            const QEdge *edge = aet.at(i);
 	    if (XFixedToDouble(edge->p2.y) < next_y)
 		next_y = XFixedToDouble(edge->p2.y);
        }

	if (et.size() && next_y > XFixedToDouble(et.at(0)->p1.y))
	    next_y = XFixedToDouble(et.at(0)->p1.y);

        int aetSize = aet.size();
	for (int i = 0; i < aetSize; ++i) {
	    for (int k = i+1; k < aetSize; ++k) {
                const QEdge *edgeI = aet.at(i);
                const QEdge *edgeK = aet.at(k);
		qreal m1 = edgeI->m;
		qreal b1 = edgeI->b;
		qreal m2 = edgeK->m;
		qreal b2 = edgeK->b;

		if (qAbs(m1 - m2) < 0.001)
                    continue;

                // ### intersect is not calculated correctly when optimized with -O2 (gcc)
                volatile qreal intersect = 0;
                if (!qIsFinite(b1))
                    intersect = (1.f / m2) * XFixedToDouble(edgeI->p1.x) + b2;
                else if (!qIsFinite(b2))
                    intersect = (1.f / m1) * XFixedToDouble(edgeK->p1.x) + b1;
                else
                    intersect = (b1*m1 - b2*m2) / (m1 - m2);

 		if (intersect > y && intersect < next_y)
		    next_y = intersect;
	    }
	}

        XFixed yf, next_yf;
        yf = qrealToXFixed(y);
        next_yf = qrealToXFixed(next_y);

        if (yf == next_yf) {
            y = currentY = next_y;
            continue;
        }

#ifdef QT_DEBUG_TESSELATOR
        qDebug("###> y = %f, next_y = %f, %d active edges", y, next_y, aet.size());
        qDebug("===> edges");
        dump_edges(et);
        qDebug("===> active edges");
        dump_edges(aet);
#endif
	// calc intersection points
 	QVarLengthArray<QIntersectionPoint> isects(aet.size()+1);
 	for (int i = 0; i < isects.size()-1; ++i) {
            const QEdge *edge = aet.at(i);
 	    isects[i].x = (edge->p1.x != edge->p2.x) ?
			  ((y - edge->b)*edge->m) : XFixedToDouble(edge->p1.x);
	    isects[i].edge = edge;
	}

	if (isects.size()%2 != 1)
	    qFatal("%s: number of intersection points must be odd", Q_FUNC_INFO);

	// sort intersection points
 	qSort(&isects[0], &isects[isects.size()-1], compareIntersections);
//         qDebug() << "INTERSECTION_POINTS:";
//  	for (int i = 0; i < isects.size(); ++i)
//             qDebug() << isects[i].edge << isects[i].x;

        if (winding) {
            // winding fill rule
            for (int i = 0; i < isects.size()-1;) {
                int winding = 0;
                const QEdge *left = isects[i].edge;
                const QEdge *right = 0;
                winding += isects[i].edge->winding;
                for (++i; i < isects.size()-1 && winding != 0; ++i) {
                    winding += isects[i].edge->winding;
                    right = isects[i].edge;
                }
                if (!left || !right)
                    break;
                //painter.addTrapezoid(&toXTrapezoid(yf, next_yf, *left, *right));
                traps->append(toXTrapezoid(yf, next_yf, *left, *right));
            }
        } else {
            // odd-even fill rule
            for (int i = 0; i < isects.size()-2; i += 2) {
                //painter.addTrapezoid(&toXTrapezoid(yf, next_yf, *isects[i].edge, *isects[i+1].edge));
                traps->append(toXTrapezoid(yf, next_yf, *isects[i].edge, *isects[i+1].edge));
            }
        }
	y = currentY = next_y;
    }

#ifdef QT_DEBUG_TESSELATOR
    qDebug("==> number of trapezoids: %d - edge table size: %d\n", traps->size(), et.size());

    for (int i = 0; i < traps->size(); ++i)
        dump_trap(traps->at(i));
#endif

    // optimize by unifying trapezoids that share left/right lines
    // and have a common top/bottom edge
//     for (int i = 0; i < tps.size(); ++i) {
// 	for (int k = i+1; k < tps.size(); ++k) {
// 	    if (i != k && tps.at(i).right == tps.at(k).right
// 		&& tps.at(i).left == tps.at(k).left
// 		&& (tps.at(i).top == tps.at(k).bottom
// 		    || tps.at(i).bottom == tps.at(k).top))
// 	    {
// 		tps[i].bottom = tps.at(k).bottom;
// 		tps.removeAt(k);
//                 i = 0;
// 		break;
// 	    }
// 	}
//     }
    //static int i = 0;
    //QImage img = painter.end();
    //img.save(QString("res%1.png").arg(i++), "PNG");
}