|
| 1 | +From 7f345f2a1c8d9f60c60e8a9d8cc90729794bd2f1 Mon Sep 17 00:00:00 2001 |
| 2 | +From: Eirik Aavitsland <eirik.aavitsland@qt.io> |
| 3 | +Date: Tue, 13 Apr 2021 14:23:45 +0200 |
| 4 | +Subject: Avoid processing-intensive painting of high number of tiny dashes |
| 5 | + |
| 6 | +When stroking a dashed path, an unnecessary amount of processing would |
| 7 | +be spent if there is a huge number of dashes visible, e.g. because of |
| 8 | +scaling. Since the dashes are too small to be indivdually visible |
| 9 | +anyway, just replace with a semi-transparent solid line for such |
| 10 | +cases. |
| 11 | + |
| 12 | +Change-Id: I9e9f7861257ad5bce46a0cf113d1a9d7824911e6 |
| 13 | +Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io> |
| 14 | +(cherry picked from commit f4d791b330d02777fcaf02938732892eb3167e9b) |
| 15 | +Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io> |
| 16 | +--- |
| 17 | + src/gui/painting/qpaintengineex.cpp | 44 +++++++++++++++++++----- |
| 18 | + tests/auto/other/lancelot/scripts/tinydashes.qps | 34 ++++++++++++++++++ |
| 19 | + 2 files changed, 69 insertions(+), 9 deletions(-) |
| 20 | + create mode 100644 tests/auto/other/lancelot/scripts/tinydashes.qps |
| 21 | + |
| 22 | +diff --git a/src/gui/painting/qpaintengineex.cpp b/src/gui/painting/qpaintengineex.cpp |
| 23 | +index f4cbf15fc7..f04e3a498d 100644 |
| 24 | +--- a/src/gui/painting/qpaintengineex.cpp |
| 25 | ++++ b/src/gui/painting/qpaintengineex.cpp |
| 26 | +@@ -385,7 +385,7 @@ QPainterState *QPaintEngineEx::createState(QPainterState *orig) const |
| 27 | + |
| 28 | + Q_GUI_EXPORT extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp |
| 29 | + |
| 30 | +-void QPaintEngineEx::stroke(const QVectorPath &path, const QPen &pen) |
| 31 | ++void QPaintEngineEx::stroke(const QVectorPath &path, const QPen &inPen) |
| 32 | + { |
| 33 | + #ifdef QT_DEBUG_DRAW |
| 34 | + qDebug() << "QPaintEngineEx::stroke()" << pen; |
| 35 | +@@ -403,6 +403,38 @@ void QPaintEngineEx::stroke(const QVectorPath &path, const QPen &pen) |
| 36 | + d->stroker.setCubicToHook(qpaintengineex_cubicTo); |
| 37 | + } |
| 38 | + |
| 39 | ++ QRectF clipRect; |
| 40 | ++ QPen pen = inPen; |
| 41 | ++ if (pen.style() > Qt::SolidLine) { |
| 42 | ++ QRectF cpRect = path.controlPointRect(); |
| 43 | ++ const QTransform &xf = state()->matrix; |
| 44 | ++ if (qt_pen_is_cosmetic(pen, state()->renderHints)) { |
| 45 | ++ clipRect = d->exDeviceRect; |
| 46 | ++ cpRect.translate(xf.dx(), xf.dy()); |
| 47 | ++ } else { |
| 48 | ++ clipRect = xf.inverted().mapRect(QRectF(d->exDeviceRect)); |
| 49 | ++ } |
| 50 | ++ // Check to avoid generating unwieldy amount of dashes that will not be visible anyway |
| 51 | ++ QRectF extentRect = cpRect & clipRect; |
| 52 | ++ qreal extent = qMax(extentRect.width(), extentRect.height()); |
| 53 | ++ qreal patternLength = 0; |
| 54 | ++ const QVector<qreal> pattern = pen.dashPattern(); |
| 55 | ++ const int patternSize = qMin(pattern.size(), 32); |
| 56 | ++ for (int i = 0; i < patternSize; i++) |
| 57 | ++ patternLength += qMax(pattern.at(i), qreal(0)); |
| 58 | ++ if (pen.widthF()) |
| 59 | ++ patternLength *= pen.widthF(); |
| 60 | ++ if (qFuzzyIsNull(patternLength)) { |
| 61 | ++ pen.setStyle(Qt::NoPen); |
| 62 | ++ } else if (extent / patternLength > 10000) { |
| 63 | ++ // approximate stream of tiny dashes with semi-transparent solid line |
| 64 | ++ pen.setStyle(Qt::SolidLine); |
| 65 | ++ QColor color(pen.color()); |
| 66 | ++ color.setAlpha(color.alpha() / 2); |
| 67 | ++ pen.setColor(color); |
| 68 | ++ } |
| 69 | ++ } |
| 70 | ++ |
| 71 | + if (!qpen_fast_equals(pen, d->strokerPen)) { |
| 72 | + d->strokerPen = pen; |
| 73 | + d->stroker.setJoinStyle(pen.joinStyle()); |
| 74 | +@@ -430,14 +462,8 @@ void QPaintEngineEx::stroke(const QVectorPath &path, const QPen &pen) |
| 75 | + return; |
| 76 | + } |
| 77 | + |
| 78 | +- if (pen.style() > Qt::SolidLine) { |
| 79 | +- if (qt_pen_is_cosmetic(pen, state()->renderHints)){ |
| 80 | +- d->activeStroker->setClipRect(d->exDeviceRect); |
| 81 | +- } else { |
| 82 | +- QRectF clipRect = state()->matrix.inverted().mapRect(QRectF(d->exDeviceRect)); |
| 83 | +- d->activeStroker->setClipRect(clipRect); |
| 84 | +- } |
| 85 | +- } |
| 86 | ++ if (!clipRect.isNull()) |
| 87 | ++ d->activeStroker->setClipRect(clipRect); |
| 88 | + |
| 89 | + const QPainterPath::ElementType *types = path.elements(); |
| 90 | + const qreal *points = path.points(); |
| 91 | +diff --git a/tests/auto/other/lancelot/scripts/tinydashes.qps b/tests/auto/other/lancelot/scripts/tinydashes.qps |
| 92 | +new file mode 100644 |
| 93 | +index 0000000000..d41ced7f5f |
| 94 | +--- /dev/null |
| 95 | ++++ b/tests/auto/other/lancelot/scripts/tinydashes.qps |
| 96 | +@@ -0,0 +1,34 @@ |
| 97 | ++# Version: 1 |
| 98 | ++# CheckVsReference: 5% |
| 99 | ++ |
| 100 | ++path_addEllipse mypath 20.0 20.0 200.0 200.0 |
| 101 | ++ |
| 102 | ++save |
| 103 | ++setPen blue 20 SolidLine FlatCap |
| 104 | ++pen_setCosmetic true |
| 105 | ++pen_setDashPattern [ 0.0004 0.0004 ] |
| 106 | ++setBrush yellow |
| 107 | ++ |
| 108 | ++drawPath mypath |
| 109 | ++translate 300 0 |
| 110 | ++setRenderHint Antialiasing true |
| 111 | ++drawPath mypath |
| 112 | ++restore |
| 113 | ++ |
| 114 | ++path_addEllipse bigpath 200000.0 200000.0 2000000.0 2000000.0 |
| 115 | ++ |
| 116 | ++setPen blue 20 DotLine FlatCap |
| 117 | ++setBrush yellow |
| 118 | ++ |
| 119 | ++save |
| 120 | ++translate 0 300 |
| 121 | ++scale 0.0001 0.00011 |
| 122 | ++drawPath bigpath |
| 123 | ++restore |
| 124 | ++ |
| 125 | ++save |
| 126 | ++translate 300 300 |
| 127 | ++setRenderHint Antialiasing true |
| 128 | ++scale 0.0001 0.00011 |
| 129 | ++drawPath bigpath |
| 130 | ++restore |
| 131 | +-- |
| 132 | +From 9378ba2ae857df7e21a384e514650823db2355c3 Mon Sep 17 00:00:00 2001 |
| 133 | +From: Eirik Aavitsland <eirik.aavitsland@qt.io> |
| 134 | +Date: Fri, 23 Jul 2021 15:53:56 +0200 |
| 135 | +Subject: Improve fix for avoiding huge number of tiny dashes |
| 136 | +MIME-Version: 1.0 |
| 137 | +Content-Type: text/plain; charset=UTF-8 |
| 138 | +Content-Transfer-Encoding: 8bit |
| 139 | + |
| 140 | +Some pathological cases were not caught by the previous fix. |
| 141 | + |
| 142 | +Fixes: QTBUG-95239 |
| 143 | +Change-Id: I0337ee3923ff93ccb36c4d7b810a9c0667354cc5 |
| 144 | +Reviewed-by: Robert Löhning <robert.loehning@qt.io> |
| 145 | +(cherry picked from commit 6b400e3147dcfd8cc3a393ace1bd118c93762e0c) |
| 146 | +Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io> |
| 147 | +--- |
| 148 | + src/gui/painting/qpaintengineex.cpp | 2 +- |
| 149 | + 1 file changed, 1 insertion(+), 1 deletion(-) |
| 150 | + |
| 151 | +diff --git a/src/gui/painting/qpaintengineex.cpp b/src/gui/painting/qpaintengineex.cpp |
| 152 | +index f04e3a498d..bd6bdf7be1 100644 |
| 153 | +--- a/src/gui/painting/qpaintengineex.cpp |
| 154 | ++++ b/src/gui/painting/qpaintengineex.cpp |
| 155 | +@@ -426,7 +426,7 @@ void QPaintEngineEx::stroke(const QVectorPath &path, const QPen &inPen) |
| 156 | + patternLength *= pen.widthF(); |
| 157 | + if (qFuzzyIsNull(patternLength)) { |
| 158 | + pen.setStyle(Qt::NoPen); |
| 159 | +- } else if (extent / patternLength > 10000) { |
| 160 | ++ } else if (qFuzzyIsNull(extent) || extent / patternLength > 10000) { |
| 161 | + // approximate stream of tiny dashes with semi-transparent solid line |
| 162 | + pen.setStyle(Qt::SolidLine); |
| 163 | + QColor color(pen.color()); |
| 164 | +-- |
| 165 | +From 81998f50d039a6317a3130ace8d4ad304f9acbbf Mon Sep 17 00:00:00 2001 |
| 166 | +From: Eirik Aavitsland <eirik.aavitsland@qt.io> |
| 167 | +Date: Fri, 30 Jul 2021 13:03:49 +0200 |
| 168 | +Subject: Refix for avoiding huge number of tiny dashes |
| 169 | + |
| 170 | +Previous fix hit too widely so some valid horizontal and vertical |
| 171 | +lines were affected; the root problem being that such lines have an |
| 172 | +empty control point rect (width or height is 0). Fix by caculating in |
| 173 | +the pen width. |
| 174 | + |
| 175 | +Change-Id: I7a436e873f6d485028f6759d0e2c6456f07eebdc |
| 176 | +Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io> |
| 177 | +(cherry picked from commit 84aba80944a2e1c3058d7a1372e0e66676411884) |
| 178 | +Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io> |
| 179 | +--- |
| 180 | + src/gui/painting/qpaintengineex.cpp | 8 +++--- |
| 181 | + tests/auto/gui/painting/qpainter/tst_qpainter.cpp | 31 +++++++++++++++++++++++ |
| 182 | + 2 files changed, 35 insertions(+), 4 deletions(-) |
| 183 | + |
| 184 | +diff --git a/src/gui/painting/qpaintengineex.cpp b/src/gui/painting/qpaintengineex.cpp |
| 185 | +index bd6bdf7be1..d8223c1b3e 100644 |
| 186 | +--- a/src/gui/painting/qpaintengineex.cpp |
| 187 | ++++ b/src/gui/painting/qpaintengineex.cpp |
| 188 | +@@ -415,18 +415,18 @@ void QPaintEngineEx::stroke(const QVectorPath &path, const QPen &inPen) |
| 189 | + clipRect = xf.inverted().mapRect(QRectF(d->exDeviceRect)); |
| 190 | + } |
| 191 | + // Check to avoid generating unwieldy amount of dashes that will not be visible anyway |
| 192 | +- QRectF extentRect = cpRect & clipRect; |
| 193 | ++ qreal pw = pen.widthF() ? pen.widthF() : 1; |
| 194 | ++ QRectF extentRect = cpRect.adjusted(-pw, -pw, pw, pw) & clipRect; |
| 195 | + qreal extent = qMax(extentRect.width(), extentRect.height()); |
| 196 | + qreal patternLength = 0; |
| 197 | + const QVector<qreal> pattern = pen.dashPattern(); |
| 198 | + const int patternSize = qMin(pattern.size(), 32); |
| 199 | + for (int i = 0; i < patternSize; i++) |
| 200 | + patternLength += qMax(pattern.at(i), qreal(0)); |
| 201 | +- if (pen.widthF()) |
| 202 | +- patternLength *= pen.widthF(); |
| 203 | ++ patternLength *= pw; |
| 204 | + if (qFuzzyIsNull(patternLength)) { |
| 205 | + pen.setStyle(Qt::NoPen); |
| 206 | +- } else if (qFuzzyIsNull(extent) || extent / patternLength > 10000) { |
| 207 | ++ } else if (extent / patternLength > 10000) { |
| 208 | + // approximate stream of tiny dashes with semi-transparent solid line |
| 209 | + pen.setStyle(Qt::SolidLine); |
| 210 | + QColor color(pen.color()); |
| 211 | +diff --git a/tests/auto/gui/painting/qpainter/tst_qpainter.cpp b/tests/auto/gui/painting/qpainter/tst_qpainter.cpp |
| 212 | +index bc0baed15c..697dcfa4a4 100644 |
| 213 | +--- a/tests/auto/gui/painting/qpainter/tst_qpainter.cpp |
| 214 | ++++ b/tests/auto/gui/painting/qpainter/tst_qpainter.cpp |
| 215 | +@@ -300,6 +300,7 @@ private slots: |
| 216 | + void fillPolygon(); |
| 217 | + |
| 218 | + void drawImageAtPointF(); |
| 219 | ++ void scaledDashes(); |
| 220 | + |
| 221 | + private: |
| 222 | + void fillData(); |
| 223 | +@@ -5308,6 +5309,36 @@ void tst_QPainter::drawImageAtPointF() |
| 224 | + paint.end(); |
| 225 | + } |
| 226 | + |
| 227 | ++void tst_QPainter::scaledDashes() |
| 228 | ++{ |
| 229 | ++ // Test that we do not hit the limit-huge-number-of-dashes path |
| 230 | ++ QRgb fore = qRgb(0, 0, 0xff); |
| 231 | ++ QRgb back = qRgb(0xff, 0xff, 0); |
| 232 | ++ QImage image(5, 32, QImage::Format_RGB32); |
| 233 | ++ image.fill(back); |
| 234 | ++ QPainter p(&image); |
| 235 | ++ QPen pen(QColor(fore), 3, Qt::DotLine); |
| 236 | ++ p.setPen(pen); |
| 237 | ++ p.scale(1, 2); |
| 238 | ++ p.drawLine(2, 0, 2, 16); |
| 239 | ++ p.end(); |
| 240 | ++ |
| 241 | ++ bool foreFound = false; |
| 242 | ++ bool backFound = false; |
| 243 | ++ int i = 0; |
| 244 | ++ while (i < 32 && (!foreFound || !backFound)) { |
| 245 | ++ QRgb pix = image.pixel(3, i); |
| 246 | ++ if (pix == fore) |
| 247 | ++ foreFound = true; |
| 248 | ++ else if (pix == back) |
| 249 | ++ backFound = true; |
| 250 | ++ i++; |
| 251 | ++ } |
| 252 | ++ |
| 253 | ++ QVERIFY(foreFound); |
| 254 | ++ QVERIFY(backFound); |
| 255 | ++} |
| 256 | ++ |
| 257 | + QTEST_MAIN(tst_QPainter) |
| 258 | + |
| 259 | + #include "tst_qpainter.moc" |
| 260 | +-- |
| 261 | +From cca8ed0547405b1c018e995ad366ba0ab4c2a0e8 Mon Sep 17 00:00:00 2001 |
| 262 | +From: Zhang Hao <zhanghao@uniontech.com> |
| 263 | +Date: Tue, 7 Sep 2021 14:50:22 +0800 |
| 264 | +Subject: QPaintEngineEx: solve compile error |
| 265 | + |
| 266 | +Use the same variable name in function |
| 267 | +Amends 6869d2463a2e0d71bd04dbc82f5d6ef4933dc510 |
| 268 | + |
| 269 | +Pick-to: 6.1 6.2 |
| 270 | +Change-Id: If710a53993e84d048f9052f4fcf92eb57635f585 |
| 271 | +Reviewed-by: Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> |
| 272 | +--- |
| 273 | + src/gui/painting/qpaintengineex.cpp | 2 +- |
| 274 | + 1 file changed, 1 insertion(+), 1 deletion(-) |
| 275 | + |
| 276 | +diff --git a/src/gui/painting/qpaintengineex.cpp b/src/gui/painting/qpaintengineex.cpp |
| 277 | +index 3577d71869..b6c0a9e1ff 100644 |
| 278 | +--- a/src/gui/painting/qpaintengineex.cpp |
| 279 | ++++ b/src/gui/painting/qpaintengineex.cpp |
| 280 | +@@ -388,7 +388,7 @@ Q_GUI_EXPORT extern bool qt_scaleForTransform(const QTransform &transform, qreal |
| 281 | + void QPaintEngineEx::stroke(const QVectorPath &path, const QPen &inPen) |
| 282 | + { |
| 283 | + #ifdef QT_DEBUG_DRAW |
| 284 | +- qDebug() << "QPaintEngineEx::stroke()" << pen; |
| 285 | ++ qDebug() << "QPaintEngineEx::stroke()" << inPen; |
| 286 | + #endif |
| 287 | + |
| 288 | + Q_D(QPaintEngineEx); |
| 289 | +-- |
0 commit comments