diff options
author | Kim Motoyoshi Kalland <kim.kalland@nokia.com> | 2012-07-05 14:52:53 +0200 |
---|---|---|
committer | Qt by Nokia <qt-info@nokia.com> | 2012-07-06 13:58:34 +0200 |
commit | d13e3441a9a3f11c7f9c9ebb21a514526987f56e (patch) | |
tree | 6c2494acd66c2b3598787fd41114b421369b548a | |
parent | 16f8afa5b1c906ae60f98619d17c34b40c21804e (diff) |
Fix division by zero in triangulating stroker.
Task-number: QTBUG-15621
Change-Id: I10e0e39e57078507a01e1c2edb59fa52fd932f6c
Reviewed-by: Gunnar Sletta <gunnar.sletta@nokia.com>
-rw-r--r-- | src/gui/opengl/qtriangulatingstroker.cpp | 68 | ||||
-rw-r--r-- | src/gui/opengl/qtriangulatingstroker_p.h | 1 | ||||
-rw-r--r-- | tests/auto/gui/qopengl/tst_qopengl.cpp | 75 |
3 files changed, 124 insertions, 20 deletions
diff --git a/src/gui/opengl/qtriangulatingstroker.cpp b/src/gui/opengl/qtriangulatingstroker.cpp index c22b731ca0..8c054290dc 100644 --- a/src/gui/opengl/qtriangulatingstroker.cpp +++ b/src/gui/opengl/qtriangulatingstroker.cpp @@ -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); } } diff --git a/src/gui/opengl/qtriangulatingstroker_p.h b/src/gui/opengl/qtriangulatingstroker_p.h index 4f36a4e6e6..532172c05d 100644 --- a/src/gui/opengl/qtriangulatingstroker_p.h +++ b/src/gui/opengl/qtriangulatingstroker_p.h @@ -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; diff --git a/tests/auto/gui/qopengl/tst_qopengl.cpp b/tests/auto/gui/qopengl/tst_qopengl.cpp index 324e1f5d12..a0671be270 100644 --- a/tests/auto/gui/qopengl/tst_qopengl.cpp +++ b/tests/auto/gui/qopengl/tst_qopengl.cpp @@ -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" |