cmannett85
cmannett85

Reputation: 22346

Scale independent Canvas

I have a QML Canvas that can be scaled (more specifically it is a child of an object that is scaled), and scaling it causes it's output to become aliased. This is occurring because the Canvas uses its size to determine the framebuffer size.

What I actually want to happen is for the framebuffer to track the Canvas size multiplied by its scale i.e. its on-screen pixel size.

My initial idea was to set the canvasWindow and canvasSize to the scaled size, but it seems that there is no decoupling between the Canvas 'pixel' size and the framebuffer's because the whole image began to render beyond the bounds of the Canvas.

In this example the black ring in rendered inside a Canvas that is parented to the scaled grey rectangle:

Before After

Canvas {
    id: borderCanvas
    anchors {
        fill: parent
        margins: 4
    }

    antialiasing: true
    canvasWindow: Qt.rect( 0,0,
                           width * parent.scale,
                           height * parent.scale );
    canvasSize: Qt.size( width * parent.scale,
                         height * parent.scale );

    onCanvasWindowChanged: {
        requestPaint();
    }

    onPaint: {
        var ctx = getContext( "2d" );
        ctx.save();
        ctx.clearRect( 0, 0, canvasWindow.width, canvasWindow.height );

        ctx.strokeStyle = Sy_application_qml.paletteObject.buttonText;
        ctx.lineWidth = 4 * parent.scale;

        ctx.beginPath();
        ctx.arc( canvasWindow.width / 2,
                 canvasWindow.height / 2,
                 ( canvasWindow.width / 2 ) - ctx.lineWidth,
                 0,
                 Math.PI * 2 );
        ctx.stroke();

        ctx.restore();
    }
}

So is there anyway to set the Canvas framebuffer size such that scaling transformations do not affect it?

Edit

If I leave canvasWindow at the default, and just update the canvasSize then I don't get the framebuffer rendering beyond the Canvas bounds - but the output is still at a low resolution so canvasSize still isn't actually changing the size of the framebuffer...

Upvotes: 2

Views: 2365

Answers (1)

cmannett85
cmannett85

Reputation: 22346

The solution was to inverse the inherited scale transformation on the Canvas, and then increase the height and width by the scale amount. This leaves the the Canvas the same size on the screen but with a framebuffer to match.

Canvas {
    id: borderCanvas
    anchors {
        top: parent.top
        left: parent.left
        margins: 4
    }

    property real viewScale: base.parent.scale

    scale: 1.0 / viewScale
    height: ( parent.height - ( anchors.margins * 2 ) ) * viewScale
    width:  ( parent.width  - ( anchors.margins * 2 ) ) * viewScale

    antialiasing: true
    transformOrigin: Item.TopLeft

    onPaint: {
        var ctx = getContext( "2d" );
        ctx.save();
        ctx.clearRect( 0, 0, width, height );

        ctx.strokeStyle = Sy_application_qml.paletteObject.buttonText;
        ctx.lineWidth = 4 * viewScale;

        ctx.beginPath();
        ctx.arc( width / 2,
                 height / 2,
                 ( width / 2 ) - ctx.lineWidth,
                 0,
                 Math.PI * 2 );
        ctx.stroke();

        ctx.restore();
    }
}

Before After

Upvotes: 4

Related Questions