From eea02ff10da0ff6bba665df560c5404477b9f550 Mon Sep 17 00:00:00 2001 From: Andrew Knight Date: Thu, 22 May 2014 08:54:14 +0300 Subject: WinRT: Support High-DPI Previously, the backing store and default framebuffer were created with the logical screen resolution (in device-independent pixels), not the the physical screen resolution. This lead to blurry text on high-DPI devices. This change fixes this by creating those at full size, and setting the device pixel ratio appropriately. Windows are still reported in device-independent pixels, but text and images are now rendered sharply for Qt Quick applications. As QPainter does not support non-integer scaling, the backing store is still drawn in DIPs and scaled by OpenGL. Task-number: QTBUG-38464 Change-Id: I7377d4c734126825d670b8ebb65fd0dd1ef705f2 Reviewed-by: Oliver Wolff --- src/plugins/platforms/winrt/qwinrtbackingstore.cpp | 22 +-- src/plugins/platforms/winrt/qwinrtbackingstore.h | 3 +- src/plugins/platforms/winrt/qwinrtscreen.cpp | 155 +++++++++++++++++---- src/plugins/platforms/winrt/qwinrtscreen.h | 22 ++- src/plugins/platforms/winrt/qwinrtwindow.cpp | 5 + src/plugins/platforms/winrt/qwinrtwindow.h | 2 + 6 files changed, 171 insertions(+), 38 deletions(-) (limited to 'src') diff --git a/src/plugins/platforms/winrt/qwinrtbackingstore.cpp b/src/plugins/platforms/winrt/qwinrtbackingstore.cpp index 9b623048af..b8418eef6a 100644 --- a/src/plugins/platforms/winrt/qwinrtbackingstore.cpp +++ b/src/plugins/platforms/winrt/qwinrtbackingstore.cpp @@ -284,7 +284,7 @@ QWinRTBackingStore::~QWinRTBackingStore() QPaintDevice *QWinRTBackingStore::paintDevice() { - return m_paintDevice.data(); + return &m_paintDevice; } void QWinRTBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoint &offset) @@ -293,8 +293,6 @@ void QWinRTBackingStore::flush(QWindow *window, const QRegion ®ion, const QPo if (m_size.isEmpty()) return; - const QImage *image = static_cast(m_paintDevice.data()); - m_context->makeCurrent(window); // Blitting the entire image width trades zero image copy/relayout for a larger texture upload. @@ -307,7 +305,7 @@ void QWinRTBackingStore::flush(QWindow *window, const QRegion ®ion, const QPo glBindTexture(GL_TEXTURE_2D, m_texture); QRect bounds = region.boundingRect(); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, bounds.y(), m_size.width(), bounds.height(), - GL_BGRA_EXT, GL_UNSIGNED_BYTE, image->scanLine(bounds.y())); + GL_BGRA_EXT, GL_UNSIGNED_BYTE, m_paintDevice.constScanLine(bounds.y())); // TODO: Implement GL_EXT_unpack_subimage in ANGLE for more minimal uploads //glPixelStorei(GL_UNPACK_ROW_LENGTH, image->bytesPerLine()); //glTexSubImage2D(GL_TEXTURE_2D, 0, bounds.x(), bounds.y(), bounds.width(), bounds.height(), @@ -325,7 +323,8 @@ void QWinRTBackingStore::flush(QWindow *window, const QRegion ®ion, const QPo glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, quadCoords); // Render - glViewport(0, 0, m_size.width(), m_size.height()); + const QSize blitSize = m_size * window->devicePixelRatio(); + glViewport(0, 0, blitSize.width(), blitSize.height()); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); // Unbind @@ -338,8 +337,8 @@ void QWinRTBackingStore::flush(QWindow *window, const QRegion ®ion, const QPo // fast blit - TODO: perform the blit inside swap buffers instead glBindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, m_fbo); glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, 0); - glBlitFramebufferANGLE(0, 0, m_size.width(), m_size.height(), // TODO: blit only the changed rectangle - 0, 0, m_size.width(), m_size.height(), + glBlitFramebufferANGLE(0, 0, blitSize.width(), blitSize.height(), // TODO: blit only the changed rectangle + 0, 0, blitSize.width(), blitSize.height(), GL_COLOR_BUFFER_BIT, GL_NEAREST); m_context->swapBuffers(window); @@ -359,21 +358,22 @@ void QWinRTBackingStore::resize(const QSize &size, const QRegion &staticContents if (m_size.isEmpty()) return; - m_paintDevice.reset(new QImage(m_size, QImage::Format_ARGB32_Premultiplied)); + m_paintDevice = QImage(m_size, QImage::Format_ARGB32_Premultiplied); m_context->makeCurrent(window()); // Input texture glBindTexture(GL_TEXTURE_2D, m_texture); glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT, m_size.width(), m_size.height(), 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, NULL); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glBindTexture(GL_TEXTURE_2D, 0); // Render buffer glBindRenderbuffer(GL_RENDERBUFFER, m_rbo); - glRenderbufferStorage(GL_RENDERBUFFER, GL_BGRA8_EXT, m_size.width(), m_size.height()); + const QSize blitSize = m_size * window()->devicePixelRatio(); + glRenderbufferStorage(GL_RENDERBUFFER, GL_BGRA8_EXT, blitSize.width(), blitSize.height()); glBindRenderbuffer(GL_RENDERBUFFER, 0); m_context->doneCurrent(); } diff --git a/src/plugins/platforms/winrt/qwinrtbackingstore.h b/src/plugins/platforms/winrt/qwinrtbackingstore.h index 726f7c838f..f00fa85a26 100644 --- a/src/plugins/platforms/winrt/qwinrtbackingstore.h +++ b/src/plugins/platforms/winrt/qwinrtbackingstore.h @@ -60,18 +60,19 @@ public: void endPaint(); void flush(QWindow *window, const QRegion ®ion, const QPoint &offset); void resize(const QSize &size, const QRegion &staticContents); + QImage toImage() const Q_DECL_OVERRIDE { return m_paintDevice; } private: bool initialize(); bool m_initialized; QSize m_size; - QScopedPointer m_paintDevice; QScopedPointer m_context; quint32 m_shaderProgram; quint32 m_fbo; quint32 m_rbo; quint32 m_texture; QWinRTScreen *m_screen; + QImage m_paintDevice; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/winrt/qwinrtscreen.cpp b/src/plugins/platforms/winrt/qwinrtscreen.cpp index e2ff7197aa..3fe856558f 100644 --- a/src/plugins/platforms/winrt/qwinrtscreen.cpp +++ b/src/plugins/platforms/winrt/qwinrtscreen.cpp @@ -93,6 +93,11 @@ typedef ITypedEventHandler PointerHandler; typedef ITypedEventHandler SizeChangedHandler; typedef ITypedEventHandler VisibilityChangedHandler; typedef ITypedEventHandler AutomationProviderRequestedHandler; +#if _MSC_VER <=1700 +typedef IDisplayPropertiesEventHandler DisplayInformationHandler; +#else +typedef ITypedEventHandler DisplayInformationHandler; +#endif #ifdef Q_OS_WINPHONE typedef IEventHandler BackPressedHandler; #endif @@ -424,12 +429,13 @@ QWinRTScreen::QWinRTScreen(ICoreWindow *window) , m_inputContext(Make(m_coreWindow).Detach()) #endif , m_cursor(new QWinRTCursor(window)) + , m_devicePixelRatio(1.0) , m_orientation(Qt::PrimaryOrientation) , m_touchDevice(Q_NULLPTR) { Rect rect; window->get_Bounds(&rect); - m_geometry = QRect(0, 0, rect.Width, rect.Height); + m_geometry = QRectF(0, 0, rect.Width, rect.Height); m_surfaceFormat.setAlphaBufferSize(0); m_surfaceFormat.setRedBufferSize(8); @@ -478,26 +484,63 @@ QWinRTScreen::QWinRTScreen(ICoreWindow *window) m_coreWindow->add_AutomationProviderRequested(Callback(this, &QWinRTScreen::onAutomationProviderRequested).Get(), &m_tokens[QEvent::InputMethodQuery]); // Orientation handling - if (SUCCEEDED(GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Graphics_Display_DisplayProperties).Get(), - &m_displayProperties))) { - // Set native orientation - DisplayOrientations displayOrientation; - m_displayProperties->get_NativeOrientation(&displayOrientation); - m_nativeOrientation = static_cast(static_cast(qtOrientationsFromNative(displayOrientation))); - - // Set initial orientation - onOrientationChanged(0); - setOrientationUpdateMask(m_nativeOrientation); - - m_displayProperties->add_OrientationChanged(Callback(this, &QWinRTScreen::onOrientationChanged).Get(), - &m_tokens[QEvent::OrientationChange]); +#if _MSC_VER<=1700 + HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Graphics_Display_DisplayProperties).Get(), + &m_displayInformation); +#else + HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Graphics_Display_DisplayInformation).Get(), + &m_displayInformationFactory); + if (FAILED(hr)) { + qErrnoWarning(hr, "Failed to get display information factory."); + return; } -#ifndef Q_OS_WINPHONE - GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_UI_ViewManagement_ApplicationView).Get(), - &m_applicationView); + hr = m_displayInformationFactory->GetForCurrentView(&m_displayInformation); #endif + if (FAILED(hr)) { + qErrnoWarning(hr, "Failed to get display information for the current view."); + return; + } + + // Set native orientation + DisplayOrientations displayOrientation; + hr = m_displayInformation->get_NativeOrientation(&displayOrientation); + if (FAILED(hr)) { + qErrnoWarning(hr, "Failed to get native orientation."); + return; + } + + m_nativeOrientation = static_cast(static_cast(qtOrientationsFromNative(displayOrientation))); + + hr = m_displayInformation->add_OrientationChanged(Callback(this, &QWinRTScreen::onOrientationChanged).Get(), + &m_tokens[QEvent::OrientationChange]); + if (FAILED(hr)) { + qErrnoWarning(hr, "Failed to add orientation change callback."); + return; + } + +#if _MSC_VER<=1700 + hr = m_displayInformation->add_LogicalDpiChanged(Callback(this, &QWinRTScreen::onDpiChanged).Get(), + &m_tokens[QEvent::Type(QEvent::User + 1)]); +#else + hr = m_displayInformation->add_DpiChanged(Callback(this, &QWinRTScreen::onDpiChanged).Get(), + &m_tokens[QEvent::Type(QEvent::User + 1)]); +#endif + if (FAILED(hr)) { + qErrnoWarning(hr, "Failed to add logical dpi change callback."); + return; + } + + // Set initial orientation & pixel density +#if _MSC_VER<=1700 + onOrientationChanged(Q_NULLPTR); + onDpiChanged(Q_NULLPTR); +#else + onOrientationChanged(Q_NULLPTR, Q_NULLPTR); + onDpiChanged(Q_NULLPTR, Q_NULLPTR); +#endif + setOrientationUpdateMask(m_nativeOrientation); if (SUCCEEDED(RoGetActivationFactory(Wrappers::HString::MakeReference(RuntimeClass_Windows_ApplicationModel_Core_CoreApplication).Get(), IID_PPV_ARGS(&m_application)))) { @@ -508,7 +551,7 @@ QWinRTScreen::QWinRTScreen(ICoreWindow *window) QRect QWinRTScreen::geometry() const { - return m_geometry; + return m_geometry.toRect(); } int QWinRTScreen::depth() const @@ -526,6 +569,21 @@ QSurfaceFormat QWinRTScreen::surfaceFormat() const return m_surfaceFormat; } +QSizeF QWinRTScreen::physicalSize() const +{ + return m_geometry.size() / m_dpi * qreal(25.4); +} + +QDpi QWinRTScreen::logicalDpi() const +{ + return QDpi(m_dpi, m_dpi); +} + +qreal QWinRTScreen::devicePixelRatio() const +{ + return m_devicePixelRatio; +} + QWinRTInputContext *QWinRTScreen::inputContext() const { return m_inputContext; @@ -572,7 +630,11 @@ Qt::ScreenOrientation QWinRTScreen::orientation() const void QWinRTScreen::setOrientationUpdateMask(Qt::ScreenOrientations mask) { - m_displayProperties->put_AutoRotationPreferences(nativeOrientationsFromQt(mask)); +#if _MSC_VER<=1700 + m_displayInformation->put_AutoRotationPreferences(nativeOrientationsFromQt(mask)); +#else + m_displayInformationFactory->put_AutoRotationPreferences(nativeOrientationsFromQt(mask)); +#endif } ICoreWindow *QWinRTScreen::coreWindow() const @@ -637,7 +699,7 @@ void QWinRTScreen::handleExpose() if (m_visibleWindows.isEmpty()) return; QList::const_iterator it = m_visibleWindows.constBegin(); - QWindowSystemInterface::handleExposeEvent(*it, m_geometry); + QWindowSystemInterface::handleExposeEvent(*it, m_geometry.toRect()); while (++it != m_visibleWindows.constEnd()) QWindowSystemInterface::handleExposeEvent(*it, QRegion()); QWindowSystemInterface::flushWindowSystemEvents(); @@ -914,9 +976,9 @@ HRESULT QWinRTScreen::onSizeChanged(ICoreWindow *window, IWindowSizeChangedEvent // Regardless of state, all top-level windows are viewport-sized - this might change if // a more advanced compositor is written. - m_geometry.setSize(QSize(size.Width, size.Height)); - QWindowSystemInterface::handleScreenGeometryChange(screen(), m_geometry); - QWindowSystemInterface::handleScreenAvailableGeometryChange(screen(), m_geometry); + m_geometry.setSize(QSizeF(size.Width, size.Height)); + QWindowSystemInterface::handleScreenGeometryChange(screen(), m_geometry.toRect()); + QWindowSystemInterface::handleScreenAvailableGeometryChange(screen(), m_geometry.toRect()); QPlatformScreen::resizeMaximizedWindows(); handleExpose(); @@ -981,10 +1043,14 @@ HRESULT QWinRTScreen::onVisibilityChanged(ICoreWindow *window, IVisibilityChange return S_OK; } +#if _MSC_VER<=1700 HRESULT QWinRTScreen::onOrientationChanged(IInspectable *) +#else +HRESULT QWinRTScreen::onOrientationChanged(IDisplayInformation *, IInspectable *) +#endif { DisplayOrientations displayOrientation; - m_displayProperties->get_CurrentOrientation(&displayOrientation); + m_displayInformation->get_CurrentOrientation(&displayOrientation); Qt::ScreenOrientation newOrientation = static_cast(static_cast(qtOrientationsFromNative(displayOrientation))); if (m_orientation != newOrientation) { m_orientation = newOrientation; @@ -994,6 +1060,47 @@ HRESULT QWinRTScreen::onOrientationChanged(IInspectable *) return S_OK; } +#if _MSC_VER<=1700 +HRESULT QWinRTScreen::onDpiChanged(IInspectable *) +#else +HRESULT QWinRTScreen::onDpiChanged(IDisplayInformation *, IInspectable *) +#endif +{ +#if defined(Q_OS_WINPHONE) && _MSC_VER>=1800 // WP 8.1 + ComPtr displayInformation; + HRESULT hr = m_displayInformation->QueryInterface(IID_IDisplayInformation2, &displayInformation); + if (FAILED(hr)) { + qErrnoWarning(hr, "Failed to cast display information."); + return S_OK; + } + hr = displayInformation->get_RawPixelsPerViewPixel(&m_devicePixelRatio); +#else + ResolutionScale resolutionScale; + HRESULT hr = m_displayInformation->get_ResolutionScale(&resolutionScale); +#endif + if (FAILED(hr)) { + qErrnoWarning(hr, "Failed to get display resolution scale."); + return S_OK; + } +#if !(defined(Q_OS_WINPHONE) && _MSC_VER>=1800) // !WP8.1 + m_devicePixelRatio = qreal(resolutionScale) / 100; +#endif + + // Correct the scale factor for integer window size + m_devicePixelRatio = m_devicePixelRatio * ((m_geometry.width()/qRound(m_geometry.width()) + + m_geometry.height()/qRound(m_geometry.height())) / 2.0); + + FLOAT dpi; + hr = m_displayInformation->get_LogicalDpi(&dpi); + if (FAILED(hr)) { + qErrnoWarning(hr, "Failed to get logical DPI."); + return S_OK; + } + m_dpi = dpi; + + return S_OK; +} + #ifdef Q_OS_WINPHONE HRESULT QWinRTScreen::onBackButtonPressed(IInspectable *, IBackPressedEventArgs *args) { diff --git a/src/plugins/platforms/winrt/qwinrtscreen.h b/src/plugins/platforms/winrt/qwinrtscreen.h index 753d89541c..d39683a960 100644 --- a/src/plugins/platforms/winrt/qwinrtscreen.h +++ b/src/plugins/platforms/winrt/qwinrtscreen.h @@ -78,6 +78,8 @@ namespace ABI { namespace Graphics { namespace Display { struct IDisplayPropertiesStatics; + struct IDisplayInformationStatics; + struct IDisplayInformation; } } #ifdef Q_OS_WINPHONE @@ -109,6 +111,9 @@ public: int depth() const; QImage::Format format() const; QSurfaceFormat surfaceFormat() const; + QSizeF physicalSize() const Q_DECL_OVERRIDE; + QDpi logicalDpi() const Q_DECL_OVERRIDE; + qreal devicePixelRatio() const Q_DECL_OVERRIDE; QWinRTInputContext *inputContext() const; QPlatformCursor *cursor() const; Qt::KeyboardModifiers keyboardModifiers() const; @@ -150,7 +155,13 @@ private: HRESULT onVisibilityChanged(ABI::Windows::UI::Core::ICoreWindow *, ABI::Windows::UI::Core::IVisibilityChangedEventArgs *args); HRESULT onAutomationProviderRequested(ABI::Windows::UI::Core::ICoreWindow *, ABI::Windows::UI::Core::IAutomationProviderRequestedEventArgs *args); +#if _MSC_VER<=1700 HRESULT onOrientationChanged(IInspectable *); + HRESULT onDpiChanged(IInspectable *); +#else + HRESULT onOrientationChanged(ABI::Windows::Graphics::Display::IDisplayInformation *, IInspectable *); + HRESULT onDpiChanged(ABI::Windows::Graphics::Display::IDisplayInformation *, IInspectable *); +#endif #ifdef Q_OS_WINPHONE HRESULT onBackButtonPressed(IInspectable *, ABI::Windows::Phone::UI::Input::IBackPressedEventArgs *args); @@ -160,9 +171,10 @@ private: ABI::Windows::UI::ViewManagement::IApplicationViewStatics *m_applicationView; ABI::Windows::ApplicationModel::Core::ICoreApplication *m_application; - QRect m_geometry; + QRectF m_geometry; QImage::Format m_format; QSurfaceFormat m_surfaceFormat; + qreal m_dpi; int m_depth; QWinRTInputContext *m_inputContext; QWinRTCursor *m_cursor; @@ -171,7 +183,13 @@ private: EGLDisplay m_eglDisplay; EGLSurface m_eglSurface; - ABI::Windows::Graphics::Display::IDisplayPropertiesStatics *m_displayProperties; +#if _MSC_VER<=1700 + ABI::Windows::Graphics::Display::IDisplayPropertiesStatics *m_displayInformation; +#else + ABI::Windows::Graphics::Display::IDisplayInformationStatics *m_displayInformationFactory; + ABI::Windows::Graphics::Display::IDisplayInformation *m_displayInformation; +#endif + qreal m_devicePixelRatio; Qt::ScreenOrientation m_nativeOrientation; Qt::ScreenOrientation m_orientation; diff --git a/src/plugins/platforms/winrt/qwinrtwindow.cpp b/src/plugins/platforms/winrt/qwinrtwindow.cpp index 88b753b463..80ee6bd761 100644 --- a/src/plugins/platforms/winrt/qwinrtwindow.cpp +++ b/src/plugins/platforms/winrt/qwinrtwindow.cpp @@ -116,4 +116,9 @@ void QWinRTWindow::lower() m_screen->lower(window()); } +qreal QWinRTWindow::devicePixelRatio() const +{ + return screen()->devicePixelRatio(); +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/winrt/qwinrtwindow.h b/src/plugins/platforms/winrt/qwinrtwindow.h index 1f19b4f2d5..121f430686 100644 --- a/src/plugins/platforms/winrt/qwinrtwindow.h +++ b/src/plugins/platforms/winrt/qwinrtwindow.h @@ -63,6 +63,8 @@ public: void raise(); void lower(); + qreal devicePixelRatio() const Q_DECL_OVERRIDE; + private: QWinRTScreen *m_screen; }; -- cgit v1.2.3