QDashStroker: cap the number of repetitions of the pattern

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 <robert.loehning@qt.io>
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
This commit is contained in:
Eirik Aavitsland 2021-08-17 13:50:50 +02:00
parent cf34fca4d0
commit 279a434c1c
4 changed files with 38 additions and 23 deletions

View File

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

View File

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

View File

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

View File

@ -268,6 +268,7 @@ public:
QStroker *stroker() const { return m_stroker; }
static QList<qfixed> patternForStyle(Qt::PenStyle style);
static int repetitionLimit() { return 10000; }
void setDashPattern(const QList<qfixed> &dashPattern) { m_dashPattern = dashPattern; }
QList<qfixed> dashPattern() const { return m_dashPattern; }