Fix division by zero in triangulating stroker.
Task-number: QTBUG-15621 Change-Id: I10e0e39e57078507a01e1c2edb59fa52fd932f6c Reviewed-by: Gunnar Sletta <gunnar.sletta@nokia.com>
This commit is contained in:
parent
16f8afa5b1
commit
d13e3441a9
@ -72,6 +72,14 @@ void QTriangulatingStroker::endCapOrJoinClosed(const qreal *start, const qreal *
|
||||
m_vertices.add(y);
|
||||
}
|
||||
|
||||
static inline void skipDuplicatePoints(const qreal **pts, const qreal *endPts)
|
||||
{
|
||||
while ((*pts + 2) < endPts && float((*pts)[0]) == float((*pts)[2])
|
||||
&& float((*pts)[1]) == float((*pts)[3]))
|
||||
{
|
||||
*pts += 2;
|
||||
}
|
||||
}
|
||||
|
||||
void QTriangulatingStroker::process(const QVectorPath &path, const QPen &pen, const QRectF &)
|
||||
{
|
||||
@ -149,67 +157,86 @@ void QTriangulatingStroker::process(const QVectorPath &path, const QPen &pen, co
|
||||
Qt::PenCapStyle cap = m_cap_style;
|
||||
|
||||
if (!types) {
|
||||
// skip duplicate points
|
||||
while((pts + 2) < endPts && pts[0] == pts[2] && pts[1] == pts[3])
|
||||
pts += 2;
|
||||
skipDuplicatePoints(&pts, endPts);
|
||||
if ((pts + 2) == endPts)
|
||||
return;
|
||||
|
||||
startPts = pts;
|
||||
|
||||
bool endsAtStart = startPts[0] == *(endPts-2) && startPts[1] == *(endPts-1);
|
||||
bool endsAtStart = float(startPts[0]) == float(endPts[-2])
|
||||
&& float(startPts[1]) == float(endPts[-1]);
|
||||
|
||||
if (endsAtStart || path.hasImplicitClose())
|
||||
m_cap_style = Qt::FlatCap;
|
||||
moveTo(pts);
|
||||
m_cap_style = cap;
|
||||
pts += 2;
|
||||
skipDuplicatePoints(&pts, endPts);
|
||||
lineTo(pts);
|
||||
pts += 2;
|
||||
skipDuplicatePoints(&pts, endPts);
|
||||
while (pts < endPts) {
|
||||
if (m_cx != pts[0] || m_cy != pts[1]) {
|
||||
join(pts);
|
||||
lineTo(pts);
|
||||
}
|
||||
join(pts);
|
||||
lineTo(pts);
|
||||
pts += 2;
|
||||
skipDuplicatePoints(&pts, endPts);
|
||||
}
|
||||
|
||||
endCapOrJoinClosed(startPts, pts-2, path.hasImplicitClose(), endsAtStart);
|
||||
|
||||
} else {
|
||||
bool endsAtStart = false;
|
||||
QPainterPath::ElementType previousType = QPainterPath::MoveToElement;
|
||||
const qreal *previousPts = pts;
|
||||
while (pts < endPts) {
|
||||
switch (*types) {
|
||||
case QPainterPath::MoveToElement: {
|
||||
if (pts != path.points())
|
||||
endCapOrJoinClosed(startPts, pts-2, path.hasImplicitClose(), endsAtStart);
|
||||
if (previousType != QPainterPath::MoveToElement)
|
||||
endCapOrJoinClosed(startPts, previousPts, path.hasImplicitClose(), endsAtStart);
|
||||
|
||||
startPts = pts;
|
||||
skipDuplicatePoints(&startPts, endPts); // Skip duplicates to find correct normal.
|
||||
if (startPts + 2 >= endPts)
|
||||
return; // Nothing to see here...
|
||||
|
||||
int end = (endPts - pts) / 2;
|
||||
int i = 2; // Start looking to ahead since we never have two moveto's in a row
|
||||
while (i<end && types[i] != QPainterPath::MoveToElement) {
|
||||
++i;
|
||||
}
|
||||
endsAtStart = startPts[0] == pts[i*2 - 2] && startPts[1] == pts[i*2 - 1];
|
||||
endsAtStart = float(startPts[0]) == float(pts[i*2 - 2])
|
||||
&& float(startPts[1]) == float(pts[i*2 - 1]);
|
||||
if (endsAtStart || path.hasImplicitClose())
|
||||
m_cap_style = Qt::FlatCap;
|
||||
|
||||
moveTo(pts);
|
||||
moveTo(startPts);
|
||||
m_cap_style = cap;
|
||||
previousType = QPainterPath::MoveToElement;
|
||||
previousPts = pts;
|
||||
pts+=2;
|
||||
++types;
|
||||
break; }
|
||||
case QPainterPath::LineToElement:
|
||||
if (*(types - 1) != QPainterPath::MoveToElement)
|
||||
join(pts);
|
||||
lineTo(pts);
|
||||
if (float(m_cx) != float(pts[0]) || float(m_cy) != float(pts[1])) {
|
||||
if (previousType != QPainterPath::MoveToElement)
|
||||
join(pts);
|
||||
lineTo(pts);
|
||||
previousType = QPainterPath::LineToElement;
|
||||
previousPts = pts;
|
||||
}
|
||||
pts+=2;
|
||||
++types;
|
||||
break;
|
||||
case QPainterPath::CurveToElement:
|
||||
if (*(types - 1) != QPainterPath::MoveToElement)
|
||||
join(pts);
|
||||
cubicTo(pts);
|
||||
if (float(m_cx) != float(pts[0]) || float(m_cy) != float(pts[1])
|
||||
|| float(pts[0]) != float(pts[2]) || float(pts[1]) != float(pts[3])
|
||||
|| float(pts[2]) != float(pts[4]) || float(pts[3]) != float(pts[5]))
|
||||
{
|
||||
if (previousType != QPainterPath::MoveToElement)
|
||||
join(pts);
|
||||
cubicTo(pts);
|
||||
previousType = QPainterPath::CurveToElement;
|
||||
previousPts = pts + 4;
|
||||
}
|
||||
pts+=6;
|
||||
types+=3;
|
||||
break;
|
||||
@ -219,7 +246,8 @@ void QTriangulatingStroker::process(const QVectorPath &path, const QPen &pen, co
|
||||
}
|
||||
}
|
||||
|
||||
endCapOrJoinClosed(startPts, pts-2, path.hasImplicitClose(), endsAtStart);
|
||||
if (previousType != QPainterPath::MoveToElement)
|
||||
endCapOrJoinClosed(startPts, previousPts, path.hasImplicitClose(), endsAtStart);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -123,6 +123,7 @@ inline void QTriangulatingStroker::normalVector(float x1, float y1, float x2, fl
|
||||
{
|
||||
float dx = x2 - x1;
|
||||
float dy = y2 - y1;
|
||||
Q_ASSERT(dx != 0 || dy != 0);
|
||||
|
||||
float pw;
|
||||
|
||||
|
@ -65,6 +65,7 @@ private slots:
|
||||
void fboHandleNulledAfterContextDestroyed();
|
||||
void openGLPaintDevice();
|
||||
void aboutToBeDestroyed();
|
||||
void QTBUG15621_triangulatingStrokerDivZero();
|
||||
};
|
||||
|
||||
struct SharedResourceTracker
|
||||
@ -535,5 +536,79 @@ void tst_QOpenGL::aboutToBeDestroyed()
|
||||
QCOMPARE(spy.size(), 1);
|
||||
}
|
||||
|
||||
void tst_QOpenGL::QTBUG15621_triangulatingStrokerDivZero()
|
||||
{
|
||||
#if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && !defined(__x86_64__)
|
||||
QSKIP("QTBUG-22617");
|
||||
#endif
|
||||
|
||||
QWindow window;
|
||||
window.setSurfaceType(QWindow::OpenGLSurface);
|
||||
window.setGeometry(0, 0, 128, 128);
|
||||
window.create();
|
||||
|
||||
QOpenGLContext ctx;
|
||||
ctx.create();
|
||||
ctx.makeCurrent(&window);
|
||||
|
||||
if (!QOpenGLFramebufferObject::hasOpenGLFramebufferObjects())
|
||||
QSKIP("QOpenGLFramebufferObject not supported on this platform");
|
||||
|
||||
QOpenGLFramebufferObject fbo(128, 128);
|
||||
fbo.bind();
|
||||
|
||||
QOpenGLPaintDevice device(128, 128);
|
||||
|
||||
// QTBUG-15621 is only a problem when qreal is double, but do the test anyway.
|
||||
qreal delta = sizeof(qreal) == sizeof(float) ? 1e-4 : 1e-8;
|
||||
QVERIFY(128 != 128 + delta);
|
||||
|
||||
QPainterPath path;
|
||||
path.moveTo(16 + delta, 16);
|
||||
path.moveTo(16, 16);
|
||||
|
||||
path.lineTo(16 + delta, 16); // Short lines to check for division by zero.
|
||||
path.lineTo(112 - delta, 16);
|
||||
path.lineTo(112, 16);
|
||||
|
||||
path.quadTo(112, 16, 112, 16 + delta);
|
||||
path.quadTo(112, 64, 112, 112 - delta);
|
||||
path.quadTo(112, 112, 112, 112);
|
||||
|
||||
path.cubicTo(112, 112, 112, 112, 112 - delta, 112);
|
||||
path.cubicTo(80, 112, 48, 112, 16 + delta, 112);
|
||||
path.cubicTo(16 + delta, 112, 16 + delta, 112, 16, 112);
|
||||
|
||||
path.closeSubpath();
|
||||
|
||||
QPen pen(Qt::red, 28, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
|
||||
|
||||
QPainter p(&device);
|
||||
p.fillRect(QRect(0, 0, 128, 128), Qt::blue);
|
||||
p.strokePath(path, pen);
|
||||
p.end();
|
||||
QImage image = fbo.toImage().convertToFormat(QImage::Format_RGB32);
|
||||
|
||||
const QRgb red = 0xffff0000;
|
||||
const QRgb blue = 0xff0000ff;
|
||||
|
||||
QCOMPARE(image.pixel(8, 8), red);
|
||||
QCOMPARE(image.pixel(119, 8), red);
|
||||
QCOMPARE(image.pixel(8, 119), red);
|
||||
QCOMPARE(image.pixel(119, 119), red);
|
||||
|
||||
QCOMPARE(image.pixel(0, 0), blue);
|
||||
QCOMPARE(image.pixel(127, 0), blue);
|
||||
QCOMPARE(image.pixel(0, 127), blue);
|
||||
QCOMPARE(image.pixel(127, 127), blue);
|
||||
|
||||
QCOMPARE(image.pixel(32, 32), blue);
|
||||
QCOMPARE(image.pixel(95, 32), blue);
|
||||
QCOMPARE(image.pixel(32, 95), blue);
|
||||
QCOMPARE(image.pixel(95, 95), blue);
|
||||
}
|
||||
|
||||
|
||||
QTEST_MAIN(tst_QOpenGL)
|
||||
|
||||
#include "tst_qopengl.moc"
|
||||
|
Loading…
Reference in New Issue
Block a user