From 279a434c1c8689f00b1ab8ed571f8732a803a7eb Mon Sep 17 00:00:00 2001 From: Eirik Aavitsland Date: Tue, 17 Aug 2021 13:50:50 +0200 Subject: [PATCH] QDashStroker: cap the number of repetitions of the pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the dashing is computed even outside the clipping and device area, painting very long dashed lines could consume unexpected amounts of time and resources. Fix by placing a limit on the dashing, and fall back to solid line drawing if hit. Fixes: QTBUG-95594 Pick-to: 6.2 6.1 5.15 Change-Id: Ida05ecd8fe6df402c9e669206fd5cec4a9f5386a Reviewed-by: Robert Löhning Reviewed-by: Allan Sandfeld Jensen --- src/gui/painting/qpaintengine_raster.cpp | 5 +++ src/gui/painting/qpaintengineex.cpp | 2 +- src/gui/painting/qstroker.cpp | 53 ++++++++++++++---------- src/gui/painting/qstroker_p.h | 1 + 4 files changed, 38 insertions(+), 23 deletions(-) diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp index 5ac1f748e5..70fce7d253 100644 --- a/src/gui/painting/qpaintengine_raster.cpp +++ b/src/gui/painting/qpaintengine_raster.cpp @@ -3265,6 +3265,11 @@ void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line, qreal length = line.length(); Q_ASSERT(length > 0); + if (length / (patternLength * width) > QDashStroker::repetitionLimit()) { + rasterizer->rasterizeLine(line.p1(), line.p2(), width / length, squareCap); + return; + } + while (length > 0) { const bool rasterize = *inDash; qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width; diff --git a/src/gui/painting/qpaintengineex.cpp b/src/gui/painting/qpaintengineex.cpp index d85c2a951c..9ca3de5cf2 100644 --- a/src/gui/painting/qpaintengineex.cpp +++ b/src/gui/painting/qpaintengineex.cpp @@ -426,7 +426,7 @@ void QPaintEngineEx::stroke(const QVectorPath &path, const QPen &inPen) patternLength *= pw; if (qFuzzyIsNull(patternLength)) { pen.setStyle(Qt::NoPen); - } else if (extent / patternLength > 10000) { + } else if (extent / patternLength > QDashStroker::repetitionLimit()) { // approximate stream of tiny dashes with semi-transparent solid line pen.setStyle(Qt::SolidLine); QColor color(pen.color()); diff --git a/src/gui/painting/qstroker.cpp b/src/gui/painting/qstroker.cpp index cd1a49150b..79194c7cf8 100644 --- a/src/gui/painting/qstroker.cpp +++ b/src/gui/painting/qstroker.cpp @@ -1179,32 +1179,41 @@ void QDashStroker::processCurrentSubpath() bool done = pos >= estop; - if (clipping) { - // Check if the entire line can be clipped away. - if (!lineIntersectsRect(prev, e, clip_tl, clip_br)) { - // Cut away full dash sequences. - elen -= qFloor(elen * invSumLength) * sumLength; - // Update dash offset. - while (!done) { - qreal dpos = pos + dashes[idash] - doffset - estart; + // Check if the entire line should be clipped away or simplified + bool clipIt = clipping && !lineIntersectsRect(prev, e, clip_tl, clip_br); + bool skipDashing = elen * invSumLength > repetitionLimit(); + if (skipDashing || clipIt) { + // Cut away full dash sequences. + elen -= std::floor(elen * invSumLength) * sumLength; + // Update dash offset. + while (!done) { + qreal dpos = pos + dashes[idash] - doffset - estart; - Q_ASSERT(dpos >= 0); + Q_ASSERT(dpos >= 0); - if (dpos > elen) { // dash extends this line - doffset = dashes[idash] - (dpos - elen); // subtract the part already used - pos = estop; // move pos to next path element - done = true; - } else { // Dash is on this line - pos = dpos + estart; - done = pos >= estop; - if (++idash >= dashCount) - idash = 0; - doffset = 0; // full segment so no offset on next. - } + if (dpos > elen) { // dash extends this line + doffset = dashes[idash] - (dpos - elen); // subtract the part already used + pos = estop; // move pos to next path element + done = true; + } else { // Dash is on this line + pos = dpos + estart; + done = pos >= estop; + if (++idash >= dashCount) + idash = 0; + doffset = 0; // full segment so no offset on next. } - hasMoveTo = false; - move_to_pos = e; } + if (clipIt) { + hasMoveTo = false; + } else { + // skip costly dashing, just draw solid line + if (!hasMoveTo) { + emitMoveTo(move_to_pos.x, move_to_pos.y); + hasMoveTo = true; + } + emitLineTo(e.x, e.y); + } + move_to_pos = e; } // Dash away... diff --git a/src/gui/painting/qstroker_p.h b/src/gui/painting/qstroker_p.h index eb0e5d7ffa..0934ef5df7 100644 --- a/src/gui/painting/qstroker_p.h +++ b/src/gui/painting/qstroker_p.h @@ -268,6 +268,7 @@ public: QStroker *stroker() const { return m_stroker; } static QList patternForStyle(Qt::PenStyle style); + static int repetitionLimit() { return 10000; } void setDashPattern(const QList &dashPattern) { m_dashPattern = dashPattern; } QList dashPattern() const { return m_dashPattern; }