diff options
author | Eirik Aavitsland <[email protected]> | 2024-05-07 16:22:53 +0200 |
---|---|---|
committer | Eirik Aavitsland <[email protected]> | 2024-05-30 20:51:46 +0200 |
commit | a5953d20e27ab73774058dd06ac514f9310a41e8 (patch) | |
tree | d57ed79a39f7430bfa5d8f1495e9ad2e6a11a249 | |
parent | 890c270b9b27de5075bda668dbe8117649673b05 (diff) |
Add QPaintDevice metric query to get precise fractional DPR value
For compatibility reasons, QPaintDevice needs to query subclasses for
device metrics as int values. To report fractional DPR values, they
have been multiplied with a large constant and divided back
afterwards. However, the loss of accuracy introduced by this, though
tiny, could still lead to rounding errors and painting artefacts when
the values where multiplied up for large coordinates.
Avoid this issue by adding a metric query that transports the full
floating point value encoded as two ints.
[ChangeLog][QtGui] Added new QPaintDevice metrics for querying
fractional device pixel ratios with high precision. Custom paintdevice
classes that support fractional DPRs are recommended to implement
support for these queries in metric(). Others can ignore them.
Fixes: QTBUG-124342
Change-Id: Ia6fa46e68e9fe981bdcbafb41daf080b4d1fb6d7
Reviewed-by: Tor Arne Vestbø <[email protected]>
-rw-r--r-- | src/gui/image/qimage.cpp | 6 | ||||
-rw-r--r-- | src/gui/image/qpixmap_blitter.cpp | 4 | ||||
-rw-r--r-- | src/gui/image/qpixmap_raster.cpp | 4 | ||||
-rw-r--r-- | src/gui/kernel/qpaintdevicewindow.cpp | 5 | ||||
-rw-r--r-- | src/gui/painting/qpaintdevice.cpp | 35 | ||||
-rw-r--r-- | src/gui/painting/qpaintdevice.h | 16 | ||||
-rw-r--r-- | src/gui/painting/qpaintdevice.qdoc | 20 | ||||
-rw-r--r-- | src/opengl/qopenglpaintdevice.cpp | 4 | ||||
-rw-r--r-- | src/openglwidgets/qopenglwidget.cpp | 4 | ||||
-rw-r--r-- | src/plugins/platforms/direct2d/qwindowsdirect2dpaintdevice.cpp | 4 | ||||
-rw-r--r-- | src/widgets/kernel/qwidget.cpp | 4 |
11 files changed, 103 insertions, 3 deletions
diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index 8998db301d2..5a2af0dbb01 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -4323,6 +4323,12 @@ int QImage::metric(PaintDeviceMetric metric) const return d->devicePixelRatio * QPaintDevice::devicePixelRatioFScale(); break; + case PdmDevicePixelRatioF_EncodedA: + Q_FALLTHROUGH(); + case PdmDevicePixelRatioF_EncodedB: + return QPaintDevice::encodeMetricF(metric, d->devicePixelRatio); + break; + default: qWarning("QImage::metric(): Unhandled metric type %d", metric); break; diff --git a/src/gui/image/qpixmap_blitter.cpp b/src/gui/image/qpixmap_blitter.cpp index 41563bf3802..925ac37be45 100644 --- a/src/gui/image/qpixmap_blitter.cpp +++ b/src/gui/image/qpixmap_blitter.cpp @@ -88,6 +88,10 @@ int QBlittablePlatformPixmap::metric(QPaintDevice::PaintDeviceMetric metric) con return devicePixelRatio(); case QPaintDevice::PdmDevicePixelRatioScaled: return devicePixelRatio() * QPaintDevice::devicePixelRatioFScale(); + case QPaintDevice::PdmDevicePixelRatioF_EncodedA: + Q_FALLTHROUGH(); + case QPaintDevice::PdmDevicePixelRatioF_EncodedB: + return QPaintDevice::encodeMetricF(metric, devicePixelRatio()); default: qWarning("QRasterPlatformPixmap::metric(): Unhandled metric type %d", metric); break; diff --git a/src/gui/image/qpixmap_raster.cpp b/src/gui/image/qpixmap_raster.cpp index 6e10bdd562f..a261bb7b5c7 100644 --- a/src/gui/image/qpixmap_raster.cpp +++ b/src/gui/image/qpixmap_raster.cpp @@ -253,6 +253,10 @@ int QRasterPlatformPixmap::metric(QPaintDevice::PaintDeviceMetric metric) const return image.devicePixelRatio(); case QPaintDevice::PdmDevicePixelRatioScaled: return image.devicePixelRatio() * QPaintDevice::devicePixelRatioFScale(); + case QPaintDevice::PdmDevicePixelRatioF_EncodedA: + Q_FALLTHROUGH(); + case QPaintDevice::PdmDevicePixelRatioF_EncodedB: + return QPaintDevice::encodeMetricF(metric, image.devicePixelRatio()); default: qWarning("QRasterPlatformPixmap::metric(): Unhandled metric type %d", metric); diff --git a/src/gui/kernel/qpaintdevicewindow.cpp b/src/gui/kernel/qpaintdevicewindow.cpp index 9e8c6ae5a85..8531aeed51d 100644 --- a/src/gui/kernel/qpaintdevicewindow.cpp +++ b/src/gui/kernel/qpaintdevicewindow.cpp @@ -136,6 +136,11 @@ int QPaintDeviceWindow::metric(PaintDeviceMetric metric) const case PdmDevicePixelRatioScaled: return int(QWindow::devicePixelRatio() * devicePixelRatioFScale()); break; + case PdmDevicePixelRatioF_EncodedA: + Q_FALLTHROUGH(); + case PdmDevicePixelRatioF_EncodedB: + return QPaintDevice::encodeMetricF(metric, QWindow::devicePixelRatio()); + break; default: break; } diff --git a/src/gui/painting/qpaintdevice.cpp b/src/gui/painting/qpaintdevice.cpp index 14e2d7cd8ea..de21901e239 100644 --- a/src/gui/painting/qpaintdevice.cpp +++ b/src/gui/painting/qpaintdevice.cpp @@ -17,6 +17,39 @@ QPaintDevice::~QPaintDevice() "painted"); } +/*! + \internal +*/ +// ### Qt 7: Replace this workaround mechanism: virtual devicePixelRatio() and virtual metricF() +inline double QPaintDevice::getDecodedMetricF(PaintDeviceMetric metricA, PaintDeviceMetric metricB) const +{ + qint32 buf[2]; + // The Encoded metric enum values come in pairs of one odd and one even value. + // We map those to the 0 and 1 indexes of buf by taking just the least significant bit. + // Same mapping here as in the encodeMetricF() function, to ensure correct order. + buf[metricA & 1] = metric(metricA); + buf[metricB & 1] = metric(metricB); + double res; + memcpy(&res, buf, sizeof(res)); + return res; +} + +qreal QPaintDevice::devicePixelRatio() const +{ + Q_STATIC_ASSERT((PdmDevicePixelRatioF_EncodedA & 1) != (PdmDevicePixelRatioF_EncodedB & 1)); + double res; + int scaledDpr = metric(PdmDevicePixelRatioScaled); + if (scaledDpr == int(devicePixelRatioFScale())) { + res = 1; // Shortcut for common case + } else if (scaledDpr == 2 * int(devicePixelRatioFScale())) { + res = 2; // Shortcut for common case + } else { + res = getDecodedMetricF(PdmDevicePixelRatioF_EncodedA, PdmDevicePixelRatioF_EncodedB); + if (res <= 0) // These metrics not implemented, fall back to PdmDevicePixelRatioScaled + res = scaledDpr / devicePixelRatioFScale(); + } + return res; +} /*! \internal @@ -64,6 +97,8 @@ int QPaintDevice::metric(PaintDeviceMetric m) const return 256; } else if (m == PdmDevicePixelRatio) { return 1; + } else if (m == PdmDevicePixelRatioF_EncodedA || m == PdmDevicePixelRatioF_EncodedB) { + return 0; } else { qDebug("Unrecognised metric %d!",m); return 0; diff --git a/src/gui/painting/qpaintdevice.h b/src/gui/painting/qpaintdevice.h index ceccac5e7ef..c39d8a059db 100644 --- a/src/gui/painting/qpaintdevice.h +++ b/src/gui/painting/qpaintdevice.h @@ -29,7 +29,9 @@ public: PdmPhysicalDpiX, PdmPhysicalDpiY, PdmDevicePixelRatio, - PdmDevicePixelRatioScaled + PdmDevicePixelRatioScaled, + PdmDevicePixelRatioF_EncodedA, + PdmDevicePixelRatioF_EncodedB }; virtual ~QPaintDevice(); @@ -46,18 +48,20 @@ public: int logicalDpiY() const { return metric(PdmDpiY); } int physicalDpiX() const { return metric(PdmPhysicalDpiX); } int physicalDpiY() const { return metric(PdmPhysicalDpiY); } - qreal devicePixelRatio() const { return metric(PdmDevicePixelRatioScaled) / devicePixelRatioFScale(); } + qreal devicePixelRatio() const; qreal devicePixelRatioF() const { return devicePixelRatio(); } int colorCount() const { return metric(PdmNumColors); } int depth() const { return metric(PdmDepth); } static inline qreal devicePixelRatioFScale() { return 0x10000; } + static inline int encodeMetricF(PaintDeviceMetric metric, double value); protected: QPaintDevice() noexcept; virtual int metric(PaintDeviceMetric metric) const; virtual void initPainter(QPainter *painter) const; virtual QPaintDevice *redirected(QPoint *offset) const; virtual QPainter *sharedPainter() const; + double getDecodedMetricF(PaintDeviceMetric metricA, PaintDeviceMetric metricB) const; ushort painters; // refcount private: @@ -80,6 +84,14 @@ inline int QPaintDevice::devType() const inline bool QPaintDevice::paintingActive() const { return painters != 0; } +inline int QPaintDevice::encodeMetricF(PaintDeviceMetric metric, double value) +{ + qint32 buf[2]; + Q_STATIC_ASSERT(sizeof(buf) == sizeof(double)); + memcpy(buf, &value, sizeof(buf)); + return buf[metric & 1]; +} + QT_END_NAMESPACE #endif // QPAINTDEVICE_H diff --git a/src/gui/painting/qpaintdevice.qdoc b/src/gui/painting/qpaintdevice.qdoc index d63fdcb92ba..9b9a6facb80 100644 --- a/src/gui/painting/qpaintdevice.qdoc +++ b/src/gui/painting/qpaintdevice.qdoc @@ -99,6 +99,14 @@ The constant scaling factor used is devicePixelRatioFScale(). This enum value has been introduced in Qt 5.6. + \value [since 6.8] PdmDevicePixelRatioF_EncodedA This enum item, together with the + corresponding \c B item, are used together for the device pixel ratio of the device, as an + encoded \c double floating point value. A QPaintDevice subclass that supports \e fractional DPR + values should implement support for these two enum items in its override of the metric() + function. The return value is expected to be the result of the encodeMetricF() function. + + \value [since 6.8] PdmDevicePixelRatioF_EncodedB See PdmDevicePixelRatioF_EncodedA. + \sa metric(), devicePixelRatio() */ @@ -288,3 +296,15 @@ \since 5.6 */ + +/*! + \fn int QPaintDevice::encodeMetricF(PaintDeviceMetric metric, double value) + + \internal + + Returns \a value encoded for the metric \a metric. Subclasses implementing metric() should use + this function to encode \value as an integer return value when the query metric specifies an + encoded floating-point value. + + \since 6.8 +*/ diff --git a/src/opengl/qopenglpaintdevice.cpp b/src/opengl/qopenglpaintdevice.cpp index fbc54c46fb9..aaee7f18fe0 100644 --- a/src/opengl/qopenglpaintdevice.cpp +++ b/src/opengl/qopenglpaintdevice.cpp @@ -247,6 +247,10 @@ int QOpenGLPaintDevice::metric(QPaintDevice::PaintDeviceMetric metric) const return d_ptr->devicePixelRatio; case PdmDevicePixelRatioScaled: return d_ptr->devicePixelRatio * QPaintDevice::devicePixelRatioFScale(); + case PdmDevicePixelRatioF_EncodedA: + Q_FALLTHROUGH(); + case PdmDevicePixelRatioF_EncodedB: + return QPaintDevice::encodeMetricF(metric, d_ptr->devicePixelRatio); default: qWarning("QOpenGLPaintDevice::metric() - metric %d not known", metric); diff --git a/src/openglwidgets/qopenglwidget.cpp b/src/openglwidgets/qopenglwidget.cpp index 4a0bf7f492e..cf70e169903 100644 --- a/src/openglwidgets/qopenglwidget.cpp +++ b/src/openglwidgets/qopenglwidget.cpp @@ -1640,6 +1640,10 @@ int QOpenGLWidget::metric(QPaintDevice::PaintDeviceMetric metric) const return QWidget::metric(metric); case PdmDevicePixelRatioScaled: return QWidget::metric(metric); + case PdmDevicePixelRatioF_EncodedA: + Q_FALLTHROUGH(); + case PdmDevicePixelRatioF_EncodedB: + return QWidget::metric(metric); default: qWarning("QOpenGLWidget::metric(): unknown metric %d", metric); return 0; diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintdevice.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintdevice.cpp index 2866c68de26..71a59cb08f5 100644 --- a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintdevice.cpp +++ b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintdevice.cpp @@ -84,10 +84,12 @@ QT_WARNING_POP case QPaintDevice::PdmDevicePixelRatio: return 1; case QPaintDevice::PdmDevicePixelRatioScaled: - return qRound(devicePixelRatioFScale()); + return int(devicePixelRatioFScale()); case QPaintDevice::PdmWidthMM: case QPaintDevice::PdmHeightMM: break; + default: + break; } return -1; diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index dfc69c7d93b..365323933b5 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -12956,6 +12956,10 @@ int QWidget::metric(PaintDeviceMetric m) const return resolveDevicePixelRatio(); case PdmDevicePixelRatioScaled: return QPaintDevice::devicePixelRatioFScale() * resolveDevicePixelRatio(); + case PdmDevicePixelRatioF_EncodedA: + Q_FALLTHROUGH(); + case PdmDevicePixelRatioF_EncodedB: + return QPaintDevice::encodeMetricF(m, resolveDevicePixelRatio()); default: break; } |