38be0d1383
This is the beginning of revision history for this module. If you want to look at revision history older than this, please refer to the Qt Git wiki for how to use Git history grafting. At the time of writing, this wiki is located here: http://qt.gitorious.org/qt/pages/GitIntroductionWithQt If you have already performed the grafting and you don't see any history beyond this commit, try running "git log" with the "--follow" argument. Branched from the monolithic repo, Qt master branch, at commit 896db169ea224deb96c59ce8af800d019de63f12
447 lines
14 KiB
C++
447 lines
14 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
|
** All rights reserved.
|
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
|
**
|
|
** This file is part of the test suite 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 "oldtessellator.h"
|
|
#include <QPointF>
|
|
#include <QVector>
|
|
#include <QList>
|
|
#include <QVariant>
|
|
#include <QVarLengthArray>
|
|
#include <qdebug.h>
|
|
|
|
#include "limits.h"
|
|
#include "utils.h"
|
|
|
|
#include "qnum.h"
|
|
#include "XrenderFake.h"
|
|
|
|
/*
|
|
* Polygon tesselator - can probably be optimized a bit more
|
|
*/
|
|
|
|
//#define QT_DEBUG_TESSELATOR
|
|
#define FloatToXFixed(i) (int)((i) * 65536)
|
|
#define IntToXFixed(i) ((i) << 16)
|
|
|
|
//inline int qrealToXFixed(qreal f)
|
|
//{ return f << 8; }
|
|
|
|
struct QEdge {
|
|
inline QEdge()
|
|
{}
|
|
inline QEdge(const QPointF &pt1,
|
|
const QPointF &pt2)
|
|
{
|
|
p1.x = XDoubleToFixed(pt1.x());
|
|
p1.y = XDoubleToFixed(pt1.y());
|
|
p2.x = XDoubleToFixed(pt2.x());
|
|
p2.y = XDoubleToFixed(pt2.y());
|
|
m = (pt1.x() - pt2.x()) ? (pt1.y() - pt2.y()) / (pt1.x() - pt2.x()) : 0;
|
|
im = m ? 1/m : 0;
|
|
b = pt1.y() - m * pt1.x();
|
|
vertical = p1.x == p2.x;
|
|
horizontal = p1.y == p2.y;
|
|
}
|
|
|
|
inline qreal xAt(const qreal &y) const
|
|
{
|
|
Q_ASSERT(p1.y != p2.y);
|
|
XFixed yf = XDoubleToFixed(y);
|
|
|
|
if (yf == p1.y)
|
|
return XFixedToDouble(p1.x);
|
|
else if (yf == p2.y)
|
|
return XFixedToDouble(p2.x);
|
|
|
|
return (!vertical) ? (((y - b)*im)) : pf1.x();
|
|
}
|
|
|
|
QPointF pf1, pf2;
|
|
XPointFixed p1, p2;
|
|
qreal m;
|
|
qreal im;
|
|
qreal b;
|
|
qreal intersection;
|
|
signed short winding;
|
|
bool vertical;
|
|
bool horizontal;
|
|
};
|
|
|
|
struct QVrtx {
|
|
typedef QList<QEdge> Edges;
|
|
XPointFixed coords;
|
|
Edges startingEdges;
|
|
Edges endingEdges;
|
|
Edges intersectingEdges;
|
|
};
|
|
|
|
struct QIntersectionPoint {
|
|
qreal x;
|
|
const QEdge *edge;
|
|
};
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
Q_DECLARE_TYPEINFO(QEdge, Q_PRIMITIVE_TYPE);
|
|
Q_DECLARE_TYPEINFO(QVrtx, Q_PRIMITIVE_TYPE);
|
|
Q_DECLARE_TYPEINFO(QIntersectionPoint, Q_PRIMITIVE_TYPE);
|
|
QT_END_NAMESPACE
|
|
|
|
|
|
// used by the edge point sort algorithm
|
|
static qreal currentY = 0.f;
|
|
|
|
static inline bool compareEdges(const QEdge *e1, const QEdge *e2)
|
|
{
|
|
return e1->p1.y < e2->p1.y;
|
|
}
|
|
|
|
static inline bool isEqual(const XPointFixed &p1, const XPointFixed &p2)
|
|
{
|
|
return ((p1.x == p2.x) && (p1.y == p2.y));
|
|
}
|
|
|
|
static inline bool compareIntersections(const QIntersectionPoint &i1, const QIntersectionPoint &i2)
|
|
{
|
|
if (qAbs(i1.x - i2.x) > 0.01) { // x != other.x in 99% of the cases
|
|
return i1.x < i2.x;
|
|
} else {
|
|
qreal x1 = (i1.edge->p1.x != i1.edge->p2.x) ?
|
|
((currentY+1 - i1.edge->b)*i1.edge->m) : XFixedToDouble(i1.edge->p1.x);
|
|
qreal x2 = (i2.edge->p1.x != i2.edge->p2.x) ?
|
|
((currentY+1 - i2.edge->b)*i2.edge->m) : XFixedToDouble(i2.edge->p1.x);
|
|
// qDebug() << ">>>" << currentY << i1.edge << i2.edge << x1 << x2;
|
|
return x1 < x2;
|
|
}
|
|
}
|
|
|
|
#ifdef QT_USE_FIXED_POINT
|
|
inline int qrealToXFixed(qreal f)
|
|
{ return f.value() << 8; }
|
|
#else
|
|
#define qrealToXFixed FloatToXFixed
|
|
#endif
|
|
|
|
static XTrapezoid QT_FASTCALL toXTrapezoid(XFixed y1, XFixed y2, const QEdge &left, const QEdge &right)
|
|
{
|
|
XTrapezoid trap;
|
|
trap.top = y1;
|
|
trap.bottom = y2;
|
|
trap.left.p1.y = left.p1.y;
|
|
trap.left.p2.y = left.p2.y;
|
|
trap.right.p1.y = right.p1.y;
|
|
trap.right.p2.y = right.p2.y;
|
|
trap.left.p1.x = left.p1.x;
|
|
trap.left.p2.x = left.p2.x;
|
|
trap.right.p1.x = right.p1.x;
|
|
trap.right.p2.x = right.p2.x;
|
|
return trap;
|
|
}
|
|
|
|
#ifdef QT_DEBUG_TESSELATOR
|
|
static void dump_edges(const QList<const QEdge *> &et)
|
|
{
|
|
for (int x = 0; x < et.size(); ++x) {
|
|
qDebug() << "edge#" << x << et.at(x) << "("
|
|
<< XFixedToDouble(et.at(x)->p1.x)
|
|
<< XFixedToDouble(et.at(x)->p1.y)
|
|
<< ") ("
|
|
<< XFixedToDouble(et.at(x)->p2.x)
|
|
<< XFixedToDouble(et.at(x)->p2.y)
|
|
<< ") b: " << et.at(x)->b << "m:" << et.at(x)->m;
|
|
}
|
|
}
|
|
|
|
static void dump_trap(const XTrapezoid &t)
|
|
{
|
|
qDebug() << "trap# t=" << t.top/65536.0 << "b=" << t.bottom/65536.0 << "h="
|
|
<< XFixedToDouble(t.bottom - t.top) << "\tleft p1: ("
|
|
<< XFixedToDouble(t.left.p1.x) << ","<< XFixedToDouble(t.left.p1.y)
|
|
<< ")" << "\tleft p2: (" << XFixedToDouble(t.left.p2.x) << ","
|
|
<< XFixedToDouble(t.left.p2.y) << ")" << "\n\t\t\t\tright p1:("
|
|
<< XFixedToDouble(t.right.p1.x) << "," << XFixedToDouble(t.right.p1.y) << ")"
|
|
<< "\tright p2:(" << XFixedToDouble(t.right.p2.x) << ","
|
|
<< XFixedToDouble(t.right.p2.y) << ")";
|
|
}
|
|
#endif
|
|
|
|
|
|
typedef int Q27Dot5;
|
|
#define Q27Dot5ToDouble(i) (i/32.)
|
|
#define FloatToQ27Dot5(i) (int)((i) * 32)
|
|
#define IntToQ27Dot5(i) ((i) << 5)
|
|
#define Q27Dot5ToXFixed(i) ((i) << 11)
|
|
#define Q27Dot5Factor 32
|
|
|
|
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);
|
|
Q_ASSERT(pg[0] == pg[pgSize-1]);
|
|
// 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;
|
|
}
|
|
|
|
Q_ASSERT(isects.size()%2 == 1);
|
|
|
|
// 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");
|
|
}
|