Development/Tutorials/Graphics/HiDPI: Difference between revisions

    From KDE TechBase
    (17 intermediate revisions by the same user not shown)
    Line 1: Line 1:
    == Introduction ==
    == Introduction ==


    HiDPI devices are very common today. Users need to scale up UI to make them looks like normal size. Common scale factors are: '''1.25''', '''1.5''', '''1.75''', '''2''', '''3'''. KDE and Qt applications may render blur icons and graphics. This guide shows how to make everything sharp and clear in HiDPI devices. Both Qt 5 Widgets and Qt 5 Quick applications are supported.
    HiDPI devices are very common today. Users need to scale up UI to make them looks like normal size. Common scale factors are: '''1.25''', '''1.5''', '''1.75''', '''2''', '''3'''. This means UI dimensions are not integers anymore. Qt provides classes and functions that accept and return <code>qreal</code> instead of <code>int</code>. They usually end with '''F'''.
     
    * QRect --> QRectF
    * QPoint --> QPointF
    * QWidget::devicePixelRatio() --> QWidget::devicePixelRatioF()
     
    KDE and Qt applications may render blur icons and graphics. This guide shows how to make everything sharp and clear in HiDPI devices. Both Qt 5 Widgets and Qt 5 Quick applications are supported.


    == Migrate to Qt 5 and KF5 ==
    == Migrate to Qt 5 and KF5 ==
    Line 15: Line 21:
    In your application's main function, add the following lines at the very beginning:
    In your application's main function, add the following lines at the very beginning:


    <SyntaxHighlight lang="c++">
    <SyntaxHighlight lang="cpp" highlight="4">
    int main(int argc, char *argv[])
    int main(int argc, char *argv[])
    {
    {
    Line 30: Line 36:
    Change all QPixmap from
    Change all QPixmap from


    <SyntaxHighlight lang="c++">
    <SyntaxHighlight lang="cpp">
    pixmap = QPixmap( width, height );
    pixmap = QPixmap( width, height );
    </SyntaxHighlight>
    </SyntaxHighlight>
    Line 36: Line 42:
    to
    to


    <SyntaxHighlight lang="c++">
    <SyntaxHighlight lang="cpp">
    pixmap = QPixmap( width * dpr, height * dpr );
    pixmap = QPixmap( width * dpr, height * dpr );
    pixmap.setDevicePixelRatio(dpr);
    pixmap.setDevicePixelRatio(dpr);
    </SyntaxHighlight>
    </SyntaxHighlight>


    So where is the <code>dpr</code> variable from? You can get it from any <code>QWidget</code> objects with function <code>devicePixelRatioF()</code>. '''F''' means the function return a float number (<code>qreal</code>).
    So where is the <code>dpr</code> variable from? You can get it from any <code>QWidget</code> objects with function <code>devicePixelRatioF()</code>. '''F''' means the function return a float number (<code>qreal</code>). If you are not in a <code>QWidget</code> class context, you can get the value via:
     
    <SyntaxHighlight lang="cpp">
    const qreal dpr = qApp->devicePixelRatio();
    </SyntaxHighlight>


    And don't forget to change <code>width</code> and <code>height</code> from <code>int</code> to <code>qreal</code>.
    And don't forget to change <code>width</code> and <code>height</code> from <code>int</code> to <code>qreal</code>.


    == QPainter ==
    == QPainter ==
    <code>QPainter</code> has various drawing functions. You need to change parameters of those functions from integer types:
    * <code>QRect</code>
    * <code>QPolygon</code>
    * <code>QPoint</code>
    * <code>int</code>
    to floating types:
    * <code>QRectF</code>
    * <code>QPolygonF</code>
    * <code>QPointF</code>
    * <code>qreal</code>
    For example, you need to change this code:
    <SyntaxHighlight lang="c++">
    QPainter p;
    p->drawText(0, 0, 12, 50, Qt::AlignLeft | Qt::AlignTop, text);
    </SyntaxHighlight>
    to:
    <SyntaxHighlight lang="c++">
    QPainter p;
    p->drawText(QRectF(0, 0, 12, 50), Qt::AlignLeft | Qt::AlignTop, text);
    </SyntaxHighlight>
    Similarly, You should set float number width for <code>QPen</code>. Please change:
    <SyntaxHighlight lang="c++">
    QPen pen(Qt::green, 3);
    </SyntaxHighlight>
    to:
    <SyntaxHighlight lang="c++">
    QPen pen(Qt::green, qreal(3));
    </SyntaxHighlight>
    After all these changes, your <code>QPainter</code> object should be able to render HiDPI graphics.
    == Qt Quick Controls 1 ==
    If the QML contains Qt Quick Controls 1, all text components will be blur and small in HiDPI display. This is likely a Qt bug.
    To solve this, we suggest you to migrate to Qt Quick Controls 2.
    == Dual-screen setup ==
    If you have dual screen setup with 1080p and 4K, set scale factor to 1.5, and put them side by side, running Qt Widgets applications in such setup will cause wrong rendering. This is likely a Qt bug. But we noticed that only some applications are affected, like Kate, Qt Creator, VirtualBox, Spectacle. Other applications look fine.
    In Spectacle, we found the cause is that when we set widget position to (0, 0) and the size is too big, Qt/X11 will move it to a position like (-640, 0), (960, -640). The positive value doesn't affect, but the negative value brings weird offset. To solve it, we simply detect position and change negative value to 0. Note this can only be done in paintEvent() rather than moveEvent(). [https://phabricator.kde.org/D24034 Check the patch here].
    In Kate, the cause is KonsolePart widget embedded in project plugin. When calling a specific function, the rendering is messed. Change to another function solved the issue. [https://invent.kde.org/kde/kate/merge_requests/19 Check the patch here].
    We haven't find a common solution for those issues. But here might be different hacks we can do.
    == Examples ==
    * [https://phabricator.kde.org/D23795 KSysGuard patch #1] (Icons)
    * [https://phabricator.kde.org/D23868 Font Manager patch] (QPainer, QPixmap, QImage)
    * [https://phabricator.kde.org/D23775 Filelight patch] (QPainer)

    Revision as of 20:31, 17 September 2019

    Introduction

    HiDPI devices are very common today. Users need to scale up UI to make them looks like normal size. Common scale factors are: 1.25, 1.5, 1.75, 2, 3. This means UI dimensions are not integers anymore. Qt provides classes and functions that accept and return qreal instead of int. They usually end with F.

    • QRect --> QRectF
    • QPoint --> QPointF
    • QWidget::devicePixelRatio() --> QWidget::devicePixelRatioF()

    KDE and Qt applications may render blur icons and graphics. This guide shows how to make everything sharp and clear in HiDPI devices. Both Qt 5 Widgets and Qt 5 Quick applications are supported.

    Migrate to Qt 5 and KF5

    Qt 4 doesn't support HiDPI rendering. Please migrate your application to Qt 5 and KF5.

    Texts

    You don't need to do anything special. Text rendering should support HiDPI out of box.

    Icons

    In your application's main function, add the following lines at the very beginning:

    int main(int argc, char *argv[])
    {
        QApplication app(argc, argv);
        app.setAttribute(Qt::AA_UseHighDpiPixmaps, true);
        ...
    }
    

    Then all icons in your application should look sharp.

    QPixmap

    Change all QPixmap from

    pixmap = QPixmap( width, height );
    

    to

    pixmap = QPixmap( width * dpr, height * dpr );
    pixmap.setDevicePixelRatio(dpr);
    

    So where is the dpr variable from? You can get it from any QWidget objects with function devicePixelRatioF(). F means the function return a float number (qreal). If you are not in a QWidget class context, you can get the value via:

    const qreal dpr = qApp->devicePixelRatio();
    

    And don't forget to change width and height from int to qreal.

    QPainter

    QPainter has various drawing functions. You need to change parameters of those functions from integer types:

    • QRect
    • QPolygon
    • QPoint
    • int

    to floating types:

    • QRectF
    • QPolygonF
    • QPointF
    • qreal

    For example, you need to change this code:

    QPainter p;
    p->drawText(0, 0, 12, 50, Qt::AlignLeft | Qt::AlignTop, text);
    

    to:

    QPainter p;
    p->drawText(QRectF(0, 0, 12, 50), Qt::AlignLeft | Qt::AlignTop, text);
    

    Similarly, You should set float number width for QPen. Please change:

    QPen pen(Qt::green, 3);
    

    to:

    QPen pen(Qt::green, qreal(3));
    

    After all these changes, your QPainter object should be able to render HiDPI graphics.

    Qt Quick Controls 1

    If the QML contains Qt Quick Controls 1, all text components will be blur and small in HiDPI display. This is likely a Qt bug.

    To solve this, we suggest you to migrate to Qt Quick Controls 2.

    Dual-screen setup

    If you have dual screen setup with 1080p and 4K, set scale factor to 1.5, and put them side by side, running Qt Widgets applications in such setup will cause wrong rendering. This is likely a Qt bug. But we noticed that only some applications are affected, like Kate, Qt Creator, VirtualBox, Spectacle. Other applications look fine.

    In Spectacle, we found the cause is that when we set widget position to (0, 0) and the size is too big, Qt/X11 will move it to a position like (-640, 0), (960, -640). The positive value doesn't affect, but the negative value brings weird offset. To solve it, we simply detect position and change negative value to 0. Note this can only be done in paintEvent() rather than moveEvent(). Check the patch here.

    In Kate, the cause is KonsolePart widget embedded in project plugin. When calling a specific function, the rendering is messed. Change to another function solved the issue. Check the patch here.

    We haven't find a common solution for those issues. But here might be different hacks we can do.

    Examples