From 900cccfd459fcbdbc4aa3d313afe12cfbf68fd87 Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Mon, 10 Jun 2013 13:50:44 +0200 Subject: Fix drawing of 0-width polylines from outside the devicerect. This was broken by a previous fix which aimed to fix gaps in polylines with tiny line segments. The result was that we skipped updating the origin point when stroke() didn't produce pixels which accidentally included the case of the line being completely outside the deviceRect. I fixed this by returning the value of clipLine in drawLine to the caller so we could still update the origin for this case. Task-number: QTBUG-31579 Change-Id: Iac29436f042da7658bbeaf9370351dc6f2c95065 Reviewed-by: Lars Knoll --- src/gui/painting/qcosmeticstroker.cpp | 42 ++++++++++++++++++++++------------- src/gui/painting/qcosmeticstroker_p.h | 2 +- 2 files changed, 27 insertions(+), 17 deletions(-) (limited to 'src/gui/painting') diff --git a/src/gui/painting/qcosmeticstroker.cpp b/src/gui/painting/qcosmeticstroker.cpp index 0f3cde3efd..39f64224f0 100644 --- a/src/gui/painting/qcosmeticstroker.cpp +++ b/src/gui/painting/qcosmeticstroker.cpp @@ -133,10 +133,15 @@ struct NoDasher { }; +/* + * The return value is the result of the clipLine() call performed at the start + * of each of the two functions, aka "false" means completely outside the devices + * rect. + */ template -static void drawLine(QCosmeticStroker *stroker, qreal x1, qreal y1, qreal x2, qreal y2, int caps); +static bool drawLine(QCosmeticStroker *stroker, qreal x1, qreal y1, qreal x2, qreal y2, int caps); template -static void drawLineAA(QCosmeticStroker *stroker, qreal x1, qreal y1, qreal x2, qreal y2, int caps); +static bool drawLineAA(QCosmeticStroker *stroker, qreal x1, qreal y1, qreal x2, qreal y2, int caps); inline void drawPixel(QCosmeticStroker *stroker, int x, int y, int coverage) { @@ -602,17 +607,20 @@ void QCosmeticStroker::drawPath(const QVectorPath &path) caps |= CapEnd; QCosmeticStroker::Point last = this->lastPixel; - stroke(this, p.x(), p.y(), p2.x(), p2.y(), caps); + bool unclipped = stroke(this, p.x(), p.y(), p2.x(), p2.y(), caps); /* fix for gaps in polylines with fastpen and aliased in a sequence of points with small distances: if current point p2 has been dropped - out, keep last non dropped point p. */ - if (fastPenAliased) { - if (last.x != lastPixel.x || last.y != lastPixel.y || - points == begin + 2 || points == end - 2 ) { - { - p = p2; - } + out, keep last non dropped point p. + + However, if the line was completely outside the devicerect, we + still need to update p to avoid drawing the line after this one from + a bad starting position. + */ + if (fastPenAliased && unclipped) { + if (last.x != lastPixel.x || last.y != lastPixel.y + || points == begin + 2 || points == end - 2) { + p = p2; } } else { p = p2; @@ -720,10 +728,10 @@ static inline void capAdjust(int caps, int &x1, int &x2, int &y, int yinc) the drawing shifts from horizontal to vertical or back. */ template -static void drawLine(QCosmeticStroker *stroker, qreal rx1, qreal ry1, qreal rx2, qreal ry2, int caps) +static bool drawLine(QCosmeticStroker *stroker, qreal rx1, qreal ry1, qreal rx2, qreal ry2, int caps) { if (stroker->clipLine(rx1, ry1, rx2, ry2)) - return; + return false; const int half = stroker->legacyRounding ? 31 : 0; int x1 = toF26Dot6(rx1) + half; @@ -813,7 +821,7 @@ static void drawLine(QCosmeticStroker *stroker, qreal rx1, qreal ry1, qreal rx2, } else { // horizontal if (!dx) - return; + return true; QCosmeticStroker::Direction dir = QCosmeticStroker::LeftToRight; @@ -886,14 +894,15 @@ static void drawLine(QCosmeticStroker *stroker, qreal rx1, qreal ry1, qreal rx2, } } stroker->lastPixel = last; + return true; } template -static void drawLineAA(QCosmeticStroker *stroker, qreal rx1, qreal ry1, qreal rx2, qreal ry2, int caps) +static bool drawLineAA(QCosmeticStroker *stroker, qreal rx1, qreal ry1, qreal rx2, qreal ry2, int caps) { if (stroker->clipLine(rx1, ry1, rx2, ry2)) - return; + return false; int x1 = toF26Dot6(rx1); int y1 = toF26Dot6(ry1); @@ -967,7 +976,7 @@ static void drawLineAA(QCosmeticStroker *stroker, qreal rx1, qreal ry1, qreal rx } else { // horizontal if (!dx) - return; + return true; int yinc = F16Dot16FixedDiv(dy, dx); @@ -1029,6 +1038,7 @@ static void drawLineAA(QCosmeticStroker *stroker, qreal rx1, qreal ry1, qreal rx drawPixel(stroker, x, (y>>16) + 1, alpha * alphaEnd >> 6); } } + return true; } QT_END_NAMESPACE diff --git a/src/gui/painting/qcosmeticstroker_p.h b/src/gui/painting/qcosmeticstroker_p.h index 05c8a2b0cf..5fc3559da4 100644 --- a/src/gui/painting/qcosmeticstroker_p.h +++ b/src/gui/painting/qcosmeticstroker_p.h @@ -53,7 +53,7 @@ QT_BEGIN_NAMESPACE class QCosmeticStroker; -typedef void (*StrokeLine)(QCosmeticStroker *stroker, qreal x1, qreal y1, qreal x2, qreal y2, int caps); +typedef bool (*StrokeLine)(QCosmeticStroker *stroker, qreal x1, qreal y1, qreal x2, qreal y2, int caps); class QCosmeticStroker { -- cgit v1.2.3 From 85b4f0d2d3ffd326548ca8f0e15ac27f5fd8d77d Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Tue, 11 Jun 2013 11:09:34 +0200 Subject: Avoid floating point exceptions in rasterizeLine. Task-number: QTBUG-16757 Change-Id: I3ef988f1e039c12529d62197d85a62f201e4ae12 Reviewed-by: Lars Knoll --- src/gui/painting/qrasterizer.cpp | 61 +++++++++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 16 deletions(-) (limited to 'src/gui/painting') diff --git a/src/gui/painting/qrasterizer.cpp b/src/gui/painting/qrasterizer.cpp index a6be7c6c78..197d49369e 100644 --- a/src/gui/painting/qrasterizer.cpp +++ b/src/gui/painting/qrasterizer.cpp @@ -734,6 +734,35 @@ static inline QPointF snapTo26Dot6Grid(const QPointF &p) qFloorF(p.y() * 64) * (1 / qreal(64))); } +/* + The rasterize line function relies on some div by zero which should + result in +/-inf values. However, when floating point exceptions are + enabled, this will cause crashes, so we return high numbers instead. + As the returned value is used in further arithmetic, returning + FLT_MAX/DBL_MAX will also cause values, so instead return a value + that is well outside the int-range. + */ +static inline qreal qSafeDivide(qreal x, qreal y) +{ + if (y == 0) + return x > 0 ? 1e20 : -1e20; + return x / y; +} + +/* Conversion to int fails if the value is too large to fit into INT_MAX or + too small to fit into INT_MIN, so we need this slightly safer conversion + when floating point exceptions are enabled + */ +static inline int qSafeFloatToQ16Dot16(qreal x) +{ + qreal tmp = x * 65536.; + if (tmp > qreal(INT_MAX)) + return INT_MAX; + else if (tmp < qreal(INT_MIN)) + return -INT_MAX; + return int(tmp); +} + void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width, bool squareCap) { if (a == b || width == 0 || d->clipRect.isEmpty()) @@ -946,23 +975,23 @@ void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width, const QPointF bottomLeftEdge = bottom - left; const QPointF bottomRightEdge = bottom - right; - const qreal topLeftSlope = topLeftEdge.x() / topLeftEdge.y(); - const qreal bottomLeftSlope = bottomLeftEdge.x() / bottomLeftEdge.y(); + const qreal topLeftSlope = qSafeDivide(topLeftEdge.x(), topLeftEdge.y()); + const qreal bottomLeftSlope = qSafeDivide(bottomLeftEdge.x(), bottomLeftEdge.y()); - const qreal topRightSlope = topRightEdge.x() / topRightEdge.y(); - const qreal bottomRightSlope = bottomRightEdge.x() / bottomRightEdge.y(); + const qreal topRightSlope = qSafeDivide(topRightEdge.x(), topRightEdge.y()); + const qreal bottomRightSlope = qSafeDivide(bottomRightEdge.x(), bottomRightEdge.y()); - const Q16Dot16 topLeftSlopeFP = FloatToQ16Dot16(topLeftSlope); - const Q16Dot16 topRightSlopeFP = FloatToQ16Dot16(topRightSlope); + const Q16Dot16 topLeftSlopeFP = qSafeFloatToQ16Dot16(topLeftSlope); + const Q16Dot16 topRightSlopeFP = qSafeFloatToQ16Dot16(topRightSlope); - const Q16Dot16 bottomLeftSlopeFP = FloatToQ16Dot16(bottomLeftSlope); - const Q16Dot16 bottomRightSlopeFP = FloatToQ16Dot16(bottomRightSlope); + const Q16Dot16 bottomLeftSlopeFP = qSafeFloatToQ16Dot16(bottomLeftSlope); + const Q16Dot16 bottomRightSlopeFP = qSafeFloatToQ16Dot16(bottomRightSlope); - const Q16Dot16 invTopLeftSlopeFP = FloatToQ16Dot16(1 / topLeftSlope); - const Q16Dot16 invTopRightSlopeFP = FloatToQ16Dot16(1 / topRightSlope); + const Q16Dot16 invTopLeftSlopeFP = qSafeFloatToQ16Dot16(qSafeDivide(1, topLeftSlope)); + const Q16Dot16 invTopRightSlopeFP = qSafeFloatToQ16Dot16(qSafeDivide(1, topRightSlope)); - const Q16Dot16 invBottomLeftSlopeFP = FloatToQ16Dot16(1 / bottomLeftSlope); - const Q16Dot16 invBottomRightSlopeFP = FloatToQ16Dot16(1 / bottomRightSlope); + const Q16Dot16 invBottomLeftSlopeFP = qSafeFloatToQ16Dot16(qSafeDivide(1, bottomLeftSlope)); + const Q16Dot16 invBottomRightSlopeFP = qSafeFloatToQ16Dot16(qSafeDivide(1, bottomRightSlope)); if (d->antialiased) { const Q16Dot16 iTopFP = IntToQ16Dot16(int(topBound)); @@ -1137,10 +1166,10 @@ void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width, int iBottom = bottom.y() < 0.5f? -1 : int(bottom.y() - 0.5f); int iMiddle = qMin(iLeft, iRight); - Q16Dot16 leftIntersectAf = FloatToQ16Dot16(top.x() + 0.5f + (iTop + 0.5f - top.y()) * topLeftSlope); - Q16Dot16 leftIntersectBf = FloatToQ16Dot16(left.x() + 0.5f + (iLeft + 1.5f - left.y()) * bottomLeftSlope); - Q16Dot16 rightIntersectAf = FloatToQ16Dot16(top.x() - 0.5f + (iTop + 0.5f - top.y()) * topRightSlope); - Q16Dot16 rightIntersectBf = FloatToQ16Dot16(right.x() - 0.5f + (iRight + 1.5f - right.y()) * bottomRightSlope); + Q16Dot16 leftIntersectAf = qSafeFloatToQ16Dot16(top.x() + 0.5f + (iTop + 0.5f - top.y()) * topLeftSlope); + Q16Dot16 leftIntersectBf = qSafeFloatToQ16Dot16(left.x() + 0.5f + (iLeft + 1.5f - left.y()) * bottomLeftSlope); + Q16Dot16 rightIntersectAf = qSafeFloatToQ16Dot16(top.x() - 0.5f + (iTop + 0.5f - top.y()) * topRightSlope); + Q16Dot16 rightIntersectBf = qSafeFloatToQ16Dot16(right.x() - 0.5f + (iRight + 1.5f - right.y()) * bottomRightSlope); int ny; int y = iTop; -- cgit v1.2.3