diff --git a/src/gui/painting/qbackingstore.cpp b/src/gui/painting/qbackingstore.cpp index ecc0d1cac2..f609cddd3c 100644 --- a/src/gui/painting/qbackingstore.cpp +++ b/src/gui/painting/qbackingstore.cpp @@ -25,11 +25,32 @@ public: { } + // Returns the DPR for the backing store. This is the DPR for the QWindow, + // possibly rounded up to the nearest integer. + qreal backingStoreDevicePixelRatio() const + { + // Note: keep in sync with QWidget::metric()! + qreal windowDpr = window->devicePixelRatio(); + return downscale ? std::ceil(windowDpr) : windowDpr; + } + + // Returns the factor used for converting from device independent to native + // backing store sizes. Normally this is just the gui scale factor, however + // if the backing store rounds the DPR up to the nearest integer then we also + // need to account for the factor introduced by that rounding. + qreal deviceIndependentToNativeFactor() const + { + const qreal roundingFactor = backingStoreDevicePixelRatio() / window->devicePixelRatio(); + const qreal guiFactor = QHighDpiScaling::factor(window); + return roundingFactor * guiFactor; + } + QWindow *window; QPlatformBackingStore *platformBackingStore = nullptr; QScopedPointer highDpiBackingstore; QRegion staticContents; QSize size; + bool downscale = qEnvironmentVariableIntValue("QT_WIDGETS_HIGHDPI_DOWNSCALE") > 0; }; /*! @@ -94,12 +115,14 @@ QWindow* QBackingStore::window() const void QBackingStore::beginPaint(const QRegion ®ion) { + const qreal dpr = d_ptr->backingStoreDevicePixelRatio(); + if (d_ptr->highDpiBackingstore && - d_ptr->highDpiBackingstore->devicePixelRatio() != d_ptr->window->devicePixelRatio()) + d_ptr->highDpiBackingstore->devicePixelRatio() != dpr) resize(size()); QPlatformBackingStore *platformBackingStore = handle(); - platformBackingStore->beginPaint(QHighDpi::toNativeLocalRegion(region, d_ptr->window)); + platformBackingStore->beginPaint(QHighDpi::scale(region, d_ptr->deviceIndependentToNativeFactor())); // When QtGui is applying a high-dpi scale factor the backing store // creates a "large" backing store image. This image needs to be @@ -118,8 +141,7 @@ void QBackingStore::beginPaint(const QRegion ®ion) d_ptr->highDpiBackingstore.reset( new QImage(source->bits(), source->width(), source->height(), source->bytesPerLine(), source->format())); - qreal targetDevicePixelRatio = d_ptr->window->devicePixelRatio(); - d_ptr->highDpiBackingstore->setDevicePixelRatio(targetDevicePixelRatio); + d_ptr->highDpiBackingstore->setDevicePixelRatio(dpr); } } } @@ -184,13 +206,15 @@ void QBackingStore::flush(const QRegion ®ion, QWindow *window, const QPoint & Q_ASSERT(window == topLevelWindow || topLevelWindow->isAncestorOf(window, QWindow::ExcludeTransients)); - QRegion nativeRegion = QHighDpi::toNativeLocalRegion(region, window); + const qreal toNativeFactor = d_ptr->deviceIndependentToNativeFactor(); + + QRegion nativeRegion = QHighDpi::scale(region, toNativeFactor); QPoint nativeOffset; if (!offset.isNull()) { - nativeOffset = QHighDpi::toNativeLocalPosition(offset, window); + nativeOffset = QHighDpi::scale(offset, toNativeFactor); // Under fractional DPR, rounding of region and offset may accumulate to an off-by-one QPoint topLeft = region.boundingRect().topLeft() + offset; - QPoint nativeTopLeft = QHighDpi::toNativeLocalPosition(topLeft, window); + QPoint nativeTopLeft = QHighDpi::scale(topLeft, toNativeFactor); QPoint diff = nativeTopLeft - (nativeRegion.boundingRect().topLeft() + nativeOffset); Q_ASSERT(qMax(qAbs(diff.x()), qAbs(diff.y())) <= 1); nativeRegion.translate(diff); @@ -206,7 +230,7 @@ void QBackingStore::flush(const QRegion ®ion, QWindow *window, const QPoint & void QBackingStore::resize(const QSize &size) { d_ptr->size = size; - handle()->resize(QHighDpi::toNativePixels(size, d_ptr->window), d_ptr->staticContents); + handle()->resize(QHighDpi::scale(size, d_ptr->deviceIndependentToNativeFactor()), d_ptr->staticContents); } /*! @@ -228,13 +252,13 @@ bool QBackingStore::scroll(const QRegion &area, int dx, int dy) // Disable scrolling for non-integer scroll deltas. For this case // the existing rendered pixels can't be re-used, and we return // false to signal that a repaint is needed. - const qreal nativeDx = QHighDpi::toNativePixels(qreal(dx), d_ptr->window); - const qreal nativeDy = QHighDpi::toNativePixels(qreal(dy), d_ptr->window); + const qreal toNativeFactor = d_ptr->deviceIndependentToNativeFactor(); + const qreal nativeDx = QHighDpi::scale(qreal(dx), toNativeFactor); + const qreal nativeDy = QHighDpi::scale(qreal(dy), toNativeFactor); if (qFloor(nativeDx) != nativeDx || qFloor(nativeDy) != nativeDy) return false; - return handle()->scroll(QHighDpi::toNativeLocalRegion(area, d_ptr->window), - nativeDx, nativeDy); + return handle()->scroll(QHighDpi::scale(area, toNativeFactor), nativeDx, nativeDy); } /*! diff --git a/src/gui/painting/qbackingstoredefaultcompositor.cpp b/src/gui/painting/qbackingstoredefaultcompositor.cpp index 949b3bc10a..bb5fe58aa8 100644 --- a/src/gui/painting/qbackingstoredefaultcompositor.cpp +++ b/src/gui/painting/qbackingstoredefaultcompositor.cpp @@ -408,7 +408,7 @@ void QBackingStoreDefaultCompositor::ensureResources(QRhiSwapChain *swapchain, Q } if (!m_sampler) { - m_sampler = m_rhi->newSampler(QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None, + m_sampler = m_rhi->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None, QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge); if (!m_sampler->create()) qWarning("QBackingStoreDefaultCompositor: Failed to create sampler"); @@ -506,7 +506,8 @@ QPlatformBackingStore::FlushResult QBackingStoreDefaultCompositor::flush(QPlatfo if (m_texture) { // The backingstore is for the entire tlw. // In case of native children offset tells the position relative to the tlw. - const QRect srcRect = toBottomLeftRect(deviceWindowRect.translated(deviceWindowOffset), m_texture->pixelSize().height()); + const QRect textureRect = QRect(QPoint(), m_texture->pixelSize()); + const QRect srcRect = toBottomLeftRect(textureRect.translated(deviceWindowOffset), m_texture->pixelSize().height()); const QMatrix3x3 source = sourceTransform(srcRect, m_texture->pixelSize(), origin); QMatrix4x4 target; // identity if (invertTargetY) diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index 01a8905c79..9c1cec5d8d 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -12698,6 +12698,17 @@ int QWidget::metric(PaintDeviceMetric m) const return QPaintDevice::metric(m); } + auto resolveDevicePixelRatio = [this, screen]() -> qreal { + + // Note: keep in sync with QBackingStorePrivate::backingStoreDevicePixelRatio()! + static bool downscale = qEnvironmentVariableIntValue("QT_WIDGETS_HIGHDPI_DOWNSCALE") > 0; + QWindow *window = this->window()->windowHandle(); + if (downscale && window) + return std::ceil(window->devicePixelRatio()); + + return screen->devicePixelRatio(); + }; + switch (m) { case PdmWidth: return data->crect.width(); @@ -12726,9 +12737,9 @@ int QWidget::metric(PaintDeviceMetric m) const case PdmPhysicalDpiY: return qRound(screen->physicalDotsPerInchY()); case PdmDevicePixelRatio: - return screen->devicePixelRatio(); + return resolveDevicePixelRatio(); case PdmDevicePixelRatioScaled: - return QPaintDevice::devicePixelRatioFScale() * screen->devicePixelRatio(); + return QPaintDevice::devicePixelRatioFScale() * resolveDevicePixelRatio(); default: break; } diff --git a/tests/manual/highdpi/dprgadget/main.cpp b/tests/manual/highdpi/dprgadget/main.cpp index 78189e8a39..9da74b4893 100644 --- a/tests/manual/highdpi/dprgadget/main.cpp +++ b/tests/manual/highdpi/dprgadget/main.cpp @@ -22,6 +22,7 @@ bool g_qtScaleFactor = false; bool g_qtUsePhysicalDpi = false; bool g_qtFontDpi = false; bool g_qtScaleFactorRoundingPolicy = false; +bool g_qtHighDpiDownscale = false; bool g_displayEvents = false; @@ -136,7 +137,7 @@ public: if (g_displayEvents) layout->addWidget(eventsLabel); - bool activeEnvironment = g_qtScaleFactor || g_qtUsePhysicalDpi || g_qtFontDpi || g_qtScaleFactorRoundingPolicy; + bool activeEnvironment = g_qtScaleFactor || g_qtUsePhysicalDpi || g_qtFontDpi || g_qtScaleFactorRoundingPolicy || g_qtHighDpiDownscale; if (activeEnvironment) { layout->addWidget(new QLabel("Active Environment:")); if (g_qtScaleFactor) { @@ -155,7 +156,10 @@ public: QString text = QString("QT_SCALE_FACTOR_ROUNDING_POLICY=") + qgetenv("QT_SCALE_FACTOR_ROUNDING_POLICY"); layout->addWidget(new QLabel(text)); } - + if (g_qtHighDpiDownscale) { + QString text = QString("QT_WIDGETS_HIGHDPI_DOWNSCALE=") + qgetenv("QT_WIDGETS_HIGHDPI_DOWNSCALE"); + layout->addWidget(new QLabel(text)); + } } auto updateValues = [=]() { @@ -244,6 +248,7 @@ int main(int argc, char **argv) { g_qtUsePhysicalDpi = qgetenv("QT_USE_PHYSICAL_DPI") == QByteArray("1"); g_qtFontDpi = qEnvironmentVariableIsSet("QT_FONT_DPI"); g_qtScaleFactorRoundingPolicy = qEnvironmentVariableIsSet("QT_SCALE_FACTOR_ROUNDING_POLICY"); + g_qtHighDpiDownscale = qEnvironmentVariableIsSet("QT_WIDGETS_HIGHDPI_DOWNSCALE"); QApplication app(argc, argv);