summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKim Motoyoshi Kalland <kim.kalland@nokia.com>2012-07-05 14:52:53 +0200
committerQt by Nokia <qt-info@nokia.com>2012-07-06 13:58:34 +0200
commitd13e3441a9a3f11c7f9c9ebb21a514526987f56e (patch)
tree6c2494acd66c2b3598787fd41114b421369b548a
parent16f8afa5b1c906ae60f98619d17c34b40c21804e (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.cpp68
-rw-r--r--src/gui/opengl/qtriangulatingstroker_p.h1
-rw-r--r--tests/auto/gui/qopengl/tst_qopengl.cpp75
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"