summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorEirik Aavitsland <eirik.aavitsland@qt.io>2022-01-21 08:05:29 +0100
committerEirik Aavitsland <eirik.aavitsland@qt.io>2022-03-03 07:49:44 +0000
commit5db7260975e3978bd5d502550f0d1308a69fb5ea (patch)
tree756483a5e75d1987419c6b50e1df9ab8e2733844 /src
parenta7ab6235f70187d345fbdc51cba353de997de44a (diff)
Fix painting clipping glitches with fractional scaling
In QPainter, clipping can only be done on whole pixels. The various ways of specifying a clipping rectangle to the QPainter API have been inconsistent in how fractional rectangles (either specified directly, or as a result of fractional scaling) are mapped (rounded) to integer coordinates. Also, the mappings have not made sure to keep the edge-to-edge property of clip rects under scaling. This is particularly important when scaling QRegions with multiple rects, as QRegion is designed on the assumption that an area can be described as a set of edge-to-edge rects. The fix rounds a clip rect identically with a fill rect. (Indeed, a followup plan would be to merge QRasterPaintEngine's toNormalizedFillRect() with the rectangle rounding function in this commit). Notably, a QRectF clip is now interpreted the same as a QPainterPath clip describing the same area. This modifies d9cc1499954829faf9486fb72056e29f1bad58e3 Task-number: QTBUG-100329 Fixes: QTBUG-95957 Task-number: QTBUG-100343 Change-Id: Iaae6464b9b17f8bf3adc69007f6ef8d623bf2c80 Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io> (cherry picked from commit 423b6509382c3bcc36eca78ced0254bfb463d017) Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/gui/kernel/qhighdpiscaling_p.h7
-rw-r--r--src/gui/painting/qmath_p.h15
-rw-r--r--src/gui/painting/qpaintengine_raster.cpp6
-rw-r--r--src/gui/painting/qtransform.cpp6
-rw-r--r--src/opengl/qopenglpaintengine.cpp2
5 files changed, 25 insertions, 11 deletions
diff --git a/src/gui/kernel/qhighdpiscaling_p.h b/src/gui/kernel/qhighdpiscaling_p.h
index 89856570ce..85527fe598 100644
--- a/src/gui/kernel/qhighdpiscaling_p.h
+++ b/src/gui/kernel/qhighdpiscaling_p.h
@@ -219,10 +219,9 @@ inline QRegion scale(const QRegion &region, qreal scaleFactor, QPoint origin = Q
if (!QHighDpiScaling::isActive())
return region;
- QRegion scaled;
- for (const QRect &rect : region)
- scaled += scale(QRectF(rect), scaleFactor, origin).toRect();
- return scaled;
+ QRegion scaled = region.translated(-origin);
+ scaled = QTransform::fromScale(scaleFactor, scaleFactor).map(scaled);
+ return scaled.translated(origin);
}
template <typename T>
diff --git a/src/gui/painting/qmath_p.h b/src/gui/painting/qmath_p.h
index 7cc3612113..e40f0d3740 100644
--- a/src/gui/painting/qmath_p.h
+++ b/src/gui/painting/qmath_p.h
@@ -52,12 +52,27 @@
//
#include <qmath.h>
+#include <qtransform.h>
QT_BEGIN_NAMESPACE
static const qreal Q_PI = qreal(M_PI); // pi
static const qreal Q_MM_PER_INCH = 25.4;
+inline QRect qt_mapFillRect(const QRectF &rect, const QTransform &xf)
+{
+ // Only for xf <= scaling or 90 degree rotations
+ Q_ASSERT(xf.type() <= QTransform::TxScale
+ || (xf.type() == QTransform::TxRotate && qFuzzyIsNull(xf.m11()) && qFuzzyIsNull(xf.m22())));
+ // Transform the corners instead of the rect to avoid hitting numerical accuracy limit
+ // when transforming topleft and size separately and adding afterwards,
+ // as that can sometimes be slightly off around the .5 point, leading to wrong rounding
+ QPoint pt1 = xf.map(rect.topLeft()).toPoint();
+ QPoint pt2 = xf.map(rect.bottomRight()).toPoint();
+ // Normalize and adjust for the QRect vs. QRectF bottomright
+ return QRect::span(pt1, pt2).adjusted(0, 0, -1, -1);
+}
+
QT_END_NAMESPACE
#endif // QMATH_P_H
diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp
index 4076795329..7ccf855b2a 100644
--- a/src/gui/painting/qpaintengine_raster.cpp
+++ b/src/gui/painting/qpaintengine_raster.cpp
@@ -49,7 +49,7 @@
#include <qpainterpath.h>
#include <qdebug.h>
#include <qbitmap.h>
-#include <qmath.h>
+#include "qmath_p.h"
#include <qrandom.h>
// #include <private/qdatabuffer_p.h>
@@ -1194,7 +1194,7 @@ void QRasterPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
#endif
const qreal *points = path.points();
QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
- if (setClipRectInDeviceCoords(s->matrix.mapRect(r).toAlignedRect(), op))
+ if (setClipRectInDeviceCoords(qt_mapFillRect(r, s->matrix), op))
return;
}
}
@@ -1254,7 +1254,7 @@ void QRasterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
QPaintEngineEx::clip(rect, op);
return;
- } else if (!setClipRectInDeviceCoords(s->matrix.mapRect(QRectF(rect)).toRect(), op)) {
+ } else if (!setClipRectInDeviceCoords(qt_mapFillRect(rect, s->matrix), op)) {
QPaintEngineEx::clip(rect, op);
return;
}
diff --git a/src/gui/painting/qtransform.cpp b/src/gui/painting/qtransform.cpp
index 9d4f28c895..c318958207 100644
--- a/src/gui/painting/qtransform.cpp
+++ b/src/gui/painting/qtransform.cpp
@@ -45,7 +45,7 @@
#include "qpainterpath.h"
#include "qpainterpath_p.h"
#include "qvariant.h"
-#include <qmath.h>
+#include "qmath_p.h"
#include <qnumeric.h>
#include <private/qbezier_p.h>
@@ -1486,12 +1486,12 @@ QRegion QTransform::map(const QRegion &r) const
QRegion res;
if (m11() < 0 || m22() < 0) {
for (const QRect &rect : r)
- res += mapRect(QRectF(rect)).toRect();
+ res += qt_mapFillRect(QRectF(rect), *this);
} else {
QVarLengthArray<QRect, 32> rects;
rects.reserve(r.rectCount());
for (const QRect &rect : r) {
- QRect nr = mapRect(QRectF(rect)).toRect();
+ QRect nr = qt_mapFillRect(QRectF(rect), *this);
if (!nr.isEmpty())
rects.append(nr);
}
diff --git a/src/opengl/qopenglpaintengine.cpp b/src/opengl/qopenglpaintengine.cpp
index c385b4c62b..98ab71dad5 100644
--- a/src/opengl/qopenglpaintengine.cpp
+++ b/src/opengl/qopenglpaintengine.cpp
@@ -2526,7 +2526,7 @@ void QOpenGL2PaintEngineEx::clip(const QVectorPath &path, Qt::ClipOperation op)
&& qFuzzyIsNull(state()->matrix.m11())
&& qFuzzyIsNull(state()->matrix.m22())))
{
- state()->rectangleClip = state()->rectangleClip.intersected(state()->matrix.mapRect(rect).toAlignedRect());
+ state()->rectangleClip &= qt_mapFillRect(rect, state()->matrix);
d->updateClipScissorTest();
return;
}