From 66c3a71e91edefdf5bc5c8efb009fb5f0f330cd1 Mon Sep 17 00:00:00 2001 From: Eirik Aavitsland Date: Thu, 17 Jan 2019 15:50:37 +0100 Subject: [PATCH] Painter path stroking: fix capping of beziers ending in tight turns For some overly tight beziers where the start or end point and the next control point are closer than the pen width, the stroker's shifting algorithm will produce a start/end tangent pointing in the opposite direction from what is expected, for one of the sides. This would break the square and round capping logic. Fix by detecting the situation in the capping function and reversing the tangent when necessary. Change-Id: I48f4f017403d7b289b0483dd2b3a7ff1bbd0cf2a Reviewed-by: Lars Knoll --- src/gui/painting/qstroker.cpp | 27 +++++++++--------- .../lancelot/scripts/degeneratebeziers.qps | 28 ++++++++++++++++++- 2 files changed, 41 insertions(+), 14 deletions(-) diff --git a/src/gui/painting/qstroker.cpp b/src/gui/painting/qstroker.cpp index 4776545be6..292952b7c0 100644 --- a/src/gui/painting/qstroker.cpp +++ b/src/gui/painting/qstroker.cpp @@ -455,12 +455,12 @@ void QStroker::joinPoints(qfixed focal_x, qfixed focal_y, const QLineF &nextLine return; } #endif + QLineF prevLine(qt_fixed_to_real(m_back2X), qt_fixed_to_real(m_back2Y), + qt_fixed_to_real(m_back1X), qt_fixed_to_real(m_back1Y)); + QPointF isect; + QLineF::IntersectType type = prevLine.intersect(nextLine, &isect); if (join == FlatJoin) { - QLineF prevLine(qt_fixed_to_real(m_back2X), qt_fixed_to_real(m_back2Y), - qt_fixed_to_real(m_back1X), qt_fixed_to_real(m_back1Y)); - QPointF isect; - QLineF::IntersectType type = prevLine.intersect(nextLine, &isect); QLineF shortCut(prevLine.p2(), nextLine.p1()); qreal angle = shortCut.angleTo(prevLine); if (type == QLineF::BoundedIntersection || (angle > 90 && !qFuzzyCompare(angle, (qreal)90))) { @@ -472,12 +472,6 @@ void QStroker::joinPoints(qfixed focal_x, qfixed focal_y, const QLineF &nextLine qt_real_to_fixed(nextLine.y1())); } else { - QLineF prevLine(qt_fixed_to_real(m_back2X), qt_fixed_to_real(m_back2Y), - qt_fixed_to_real(m_back1X), qt_fixed_to_real(m_back1Y)); - - QPointF isect; - QLineF::IntersectType type = prevLine.intersect(nextLine, &isect); - if (join == MiterJoin) { qreal appliedMiterLimit = qt_fixed_to_real(m_strokeWidth * m_miterLimit); @@ -512,7 +506,11 @@ void QStroker::joinPoints(qfixed focal_x, qfixed focal_y, const QLineF &nextLine qfixed offset = m_strokeWidth / 2; QLineF l1(prevLine); - l1.translate(l1.dx(), l1.dy()); + qreal dp = QPointF::dotProduct(QPointF(prevLine.dx(), prevLine.dy()), QPointF(nextLine.dx(), nextLine.dy())); + if (dp > 0) // same direction, means that prevLine is from a bezier that has been "reversed" by shifting + l1 = QLineF(prevLine.p2(), prevLine.p1()); + else + l1.translate(l1.dx(), l1.dy()); l1.setLength(qt_fixed_to_real(offset)); QLineF l2(nextLine.p2(), nextLine.p1()); l2.translate(l2.dx(), l2.dy()); @@ -570,7 +568,11 @@ void QStroker::joinPoints(qfixed focal_x, qfixed focal_y, const QLineF &nextLine // first control line QLineF l1 = prevLine; - l1.translate(l1.dx(), l1.dy()); + qreal dp = QPointF::dotProduct(QPointF(prevLine.dx(), prevLine.dy()), QPointF(nextLine.dx(), nextLine.dy())); + if (dp > 0) // same direction, means that prevLine is from a bezier that has been "reversed" by shifting + l1 = QLineF(prevLine.p2(), prevLine.p1()); + else + l1.translate(l1.dx(), l1.dy()); l1.setLength(QT_PATH_KAPPA * offset); // second control line, find through normal between prevLine and focal. @@ -705,7 +707,6 @@ template bool qt_stroke_side(Iterator *it, QPointF(qt_fixed_to_real(e.x), qt_fixed_to_real(e.y)), QPointF(qt_fixed_to_real(cp2.x), qt_fixed_to_real(cp2.y)), QPointF(qt_fixed_to_real(ep.x), qt_fixed_to_real(ep.y))); - int count = bezier.shifted(offsetCurves, MAX_OFFSET, offset, diff --git a/tests/auto/other/lancelot/scripts/degeneratebeziers.qps b/tests/auto/other/lancelot/scripts/degeneratebeziers.qps index fb223d5b1f..6c069fd82f 100644 --- a/tests/auto/other/lancelot/scripts/degeneratebeziers.qps +++ b/tests/auto/other/lancelot/scripts/degeneratebeziers.qps @@ -7,4 +7,30 @@ path_cubicTo degenerate 3427.0918499999997948 3872.1318999999994048 4729.4590867 scale 0.05 0.05 translate -2500 -3000 setPen black 800 -drawPath degenerate \ No newline at end of file +drawPath degenerate + +resetMatrix +path_moveTo revbez 0 20 +path_cubicTo revbez 0 0 120 0 120 -20 + +path_moveTo revbez 0 80 +path_cubicTo revbez 0 100 120 100 120 120 + +translate 50 250 + +setPen blue 40 solidline flatcap +drawPath revbez +setPen red 0 +drawPath revbez + +translate 200 0 +setPen blue 40 solidline squarecap +drawPath revbez +setPen red 0 +drawPath revbez + +translate 200 0 +setPen blue 40 solidline roundcap +drawPath revbez +setPen red 0 +drawPath revbez