Why the opengl painting is blur on High DPI display
-
As the picture showes the text and the lines are very blur, this is on a windows systemc 4K display with 200% system scale
I a using qt6.8. Thank.
it can be reproduced by bleow code:#include <QApplication> #include <QWidget> #include <QPainter> #include <QOpenGLContext> #include <QOpenGLFunctions> #include <QOpenGLFramebufferObject> #include <QOpenGLPaintDevice> #include <QSurfaceFormat> #include <QOffscreenSurface> #include <QWindow> #include <QResizeEvent> #include <QDebug> class FBOWidget : public QWidget { public: FBOWidget(QWidget *parent = nullptr) : QWidget(parent), m_fbo(nullptr), m_context(nullptr), m_surface(nullptr), m_fboImage(nullptr) { // Don't use WA_PaintOnScreen as we want to use QPainter setAttribute(Qt::WA_NoSystemBackground); setAutoFillBackground(false); // Set up OpenGL context m_context = new QOpenGLContext(this); QSurfaceFormat format; format.setRenderableType(QSurfaceFormat::OpenGL); format.setVersion(3, 3); // Use OpenGL 3.3 Core format.setProfile(QSurfaceFormat::CoreProfile); m_context->setFormat(format); if (!m_context->create()) { qCritical() << "Failed to create OpenGL context"; return; } // Create offscreen surface for the OpenGL context m_surface = new QOffscreenSurface(); m_surface->setFormat(m_context->format()); m_surface->create(); if (!m_surface->isValid()) { qCritical() << "Failed to create offscreen surface"; return; } // Set a reasonable minimum size resize(400, 400); } ~FBOWidget() { // Clean up OpenGL resources if (m_context && m_surface && m_context->makeCurrent(m_surface)) { delete m_fbo; m_context->doneCurrent(); } delete m_fboImage; delete m_context; delete m_surface; } protected: void resizeEvent(QResizeEvent *event) override { // Recreate FBO on resize if (m_context && m_surface && m_context->makeCurrent(m_surface)) { delete m_fbo; m_fbo = new QOpenGLFramebufferObject(size(), QOpenGLFramebufferObject::NoAttachment); // Make sure to update the image next time we paint delete m_fboImage; m_fboImage = nullptr; m_context->doneCurrent(); } QWidget::resizeEvent(event); } void paintEvent(QPaintEvent *event) override { Q_UNUSED(event); if (!m_context || !m_surface) return; // Initialize FBO if it doesn't exist yet if (!m_fbo && m_context->makeCurrent(m_surface)) { m_fbo = new QOpenGLFramebufferObject(size(), QOpenGLFramebufferObject::NoAttachment); m_context->doneCurrent(); } if (!m_fbo) return; // Only render to FBO if we need to update the image if (!m_fboImage) { if (m_context->makeCurrent(m_surface)) { QOpenGLFunctions *f = m_context->functions(); // Bind FBO m_fbo->bind(); // Clear with a background color f->glClearColor(0.1f, 0.1f, 0.1f, 1.0f); f->glClear(GL_COLOR_BUFFER_BIT); // Draw using QPainter on the OpenGL paint device { // Create a paint device for the FBO QOpenGLPaintDevice device(m_fbo->size()); // Create painter for the device QPainter painter(&device); painter.setRenderHint(QPainter::Antialiasing); // Set font for text QFont font("Arial", 16, QFont::Bold); painter.setFont(font); // Calculate positions int radius = qMin(width(), height()) / 3; QPoint center(width() / 2, height() / 2); // Draw text above the circle painter.setPen(Qt::white); QString text = "Qt6 FBO Demo"; QFontMetrics fm(font); int textWidth = fm.horizontalAdvance(text); int textHeight = fm.height(); painter.drawText( center.x() - textWidth / 2, center.y() - radius - textHeight, text ); // Draw a circle in the center // Circle already calculated above painter.setPen(Qt::NoPen); painter.setBrush(QColor(0, 120, 215)); painter.drawEllipse(center, radius, radius); // End painting on OpenGL device painter.end(); } // Release FBO m_fbo->release(); // Store the rendered image delete m_fboImage; m_fboImage = new QImage(m_fbo->toImage()); m_context->doneCurrent(); } } // Now draw the stored image to the widget using regular QPainter if (m_fboImage) { QPainter widgetPainter(this); widgetPainter.drawImage(0, 0, *m_fboImage); } } private: QOpenGLFramebufferObject *m_fbo; QOpenGLContext *m_context; QOffscreenSurface *m_surface; QImage *m_fboImage; // Cache the rendered image }; int main(int argc, char *argv[]) { QApplication app(argc, argv); // Set default surface format for the application QSurfaceFormat format; format.setRenderableType(QSurfaceFormat::OpenGL); format.setVersion(3, 3); format.setProfile(QSurfaceFormat::CoreProfile); format.setSamples(16); QSurfaceFormat::setDefaultFormat(format); FBOWidget widget; widget.setWindowTitle("Qt6 FBO Demo"); widget.show(); return app.exec(); }
cmake_minimum_required(VERSION 3.16)
project(qt6_fbo_demo LANGUAGES CXX)set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)Set the path to Qt6
set(CMAKE_PREFIX_PATH "${CMAKE_PREFIX_PATH};${HOME}/qt/6.8.0/macos/lib/cmake")
Find required Qt packages
find_package(Qt6 REQUIRED COMPONENTS Core Widgets OpenGL OpenGLWidgets)
Set automatic moc, uic, and rcc processing
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)Add executable
add_executable(${PROJECT_NAME} main.cpp)
Link Qt libraries
target_link_libraries(${PROJECT_NAME} PRIVATE
Qt6::Core
Qt6::Widgets
Qt6::OpenGL
Qt6::OpenGLWidgets
)OS X specific settings
if(APPLE)
set_target_properties(${PROJECT_NAME} PROPERTIES
MACOSX_BUNDLE TRUE
)
endif() -
You may need to add the following settings:
// Enable High-DPI scaling (MUST be set before QApplication) QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); // Auto-scaling QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); // Sharp icons/images QApplication app(argc, argv);
Your code works just fine on regular screens.