Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is necessary to make QtQuick 2's Canvas element HiDPI-(retina-)aware?

I have the following qml application:

import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Window 2.0

ApplicationWindow {
    id: window
    width: 480
    height: 240


    RowLayout {

        Rectangle {
            width: window.height
            height: window.height
            radius: window.height / 2
            color: "black"
        }

        Canvas {
            id: canvas
            width: window.height
            height: window.height

            onPaint: {
                var ctx = canvas.getContext('2d');
                var originX = window.height / 2
                var originY = window.height / 2
                var radius = window.height / 2

                ctx.save();

                ctx.beginPath();
                ctx.arc(originX, originY, radius, 0, 2 * Math.PI);
                ctx.fillStyle = Qt.rgba(0, 0, 0, 1);
                ctx.fill();

                ctx.restore();
            }

        }
    }
}

This produces two black circles next to each other. The left one (the Rectangle) is crisp on a Retina display, while the right one (the Canvas) is quite blurry. If I add

                antialiasing: false

to the Canvas, it produces coarse blurry pixels.

What do I need to do to make the Canvas HiDPI-aware?

(I'm using Qt 5.2.0 beta 1 on Mac OS X 10.8)


Edit: The workaround I came up with was to wrap the Canvas in an Item, scale everything up inside onPaint and then use a transform on the Canvas the scale it back down.

    Canvas {
        id: canvas
        x: 0
        y: 0
        width: parent.width * 2   // really parent.width after the transform
        heigth: parent.height * 2 // really parent.height after the transform

        /* ... */

        // This scales everything down by a factor of two.
        transform: Scale {
            xScale: .5
            yScale: .5
        }

        onPaint: {
            var ctx = canvas.getContext('2d');
            ctx.save();
            ctx.scale(2, 2)       // This scales everything up by a factor of two.

            /* ... */
        }
    }
like image 977
Tobias Avatar asked Oct 24 '13 18:10

Tobias


1 Answers

We used the same trick of doubling the size and then scaling down for the ProgressCircle in qml-material. However, there are a couple improvements you can make:

  1. Use scale instead of transform.
  2. Use Screen.devicePixelRatio from the QtQuick.Window module instead of hardcoding the scale factor at 2/0.5.

So your code can be simplified to:

Canvas {
    property int ratio: Screen.devicePixelRatio

    width: parent.width * ratio
    heigth: parent.height * ratio
    scale: 1/ratio

    onPaint: {
        var ctx = canvas.getContext('2d');
        ctx.save();
        ctx.scale(ratio, ratio)

        // ...
    }
}
like image 133
iBelieve Avatar answered Nov 06 '22 10:11

iBelieve