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:
Kim Motoyoshi Kalland 2012-07-05 14:52:53 +02:00 committed by Qt by Nokia
parent 16f8afa5b1
commit d13e3441a9
3 changed files with 124 additions and 20 deletions

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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"