SVG rasterization issue in a QML Canvas - qt

I am experiencing issues when using the QML Canvas drawImage method to render SVG icons.
Basically the QML Canvas ignores the rasterization size and paint the image starting from its original size (in this case 48x48px), and scaling it up with an ugly aliasing effect.
Has anyone ever seen this and possibly figured out a solution ?
Please see QTBUG-59878 for a working self-contained example.
Here I'm gonna copy just the QML code and a screenshot of what I see.
From the left:
the original SVG icon as Image item (correct)
the same Image item rendered by drawImage (to be honest I expected this to be correct as well, since rasterized data should be provided by Image)
the SVG icon loaded with the procedure explained in the documentation, with loadImage and onImageLoaded
import QtQuick 2.0
Rectangle
{
width: 640
height: 480
Image
{
id: svgIcon
source: "qrc:/qt_logo.svg"
x: 10
y: 100
width: 180
height: 200
sourceSize: Qt.size(width, height)
}
Canvas
{
id: canvas
anchors.fill: parent
contextType: "2d"
property string imagefile: "qrc:/qt_logo.svg"
Component.onCompleted: context.loadImage(imagefile)
onImageLoaded: requestPaint()
onPaint:
{
context.drawImage(svgIcon, 200, 100, 180, 200)
context.drawImage(imagefile, 400, 100, 180, 200)
}
}
}

I have worked with SVG within QML, the performance is very poor and quality of upscaling and downscaling isn't good enough. if you're using SVG for showing icons, i will suggest you to use fonts instead of svg icons, its pretty fast and high quality. you can convert your svg icons to font characters, and pack them in single font and use it within QML as Text or label elements, seen Iconmoon.io

Related

Image size vs sourceSize strange things

We had a strange problem with blurred images under Retina displays. Left part of the image - before, right one - after the fix.
Our QML code was using this code to show images:
Image {
anchors.verticalCenter: parent.verticalCenter
sourceSize.width: 25
sourceSize.height: 25
source: preview.url
}
I've tried to multiply sourceSize by Screen.devicePixelRatio - images became bigger so they did not fit their places.
Then I've replaced sourceSize.width with just width and the same for height. So:
Image {
anchors.verticalCenter: parent.verticalCenter
width: 25
height: 25
source: preview.url
}
And it works fine now.
My questions are:
Is it required to multiply sourceSize by devicePixelRatio? Or is it managed automatically? It seems that it is managed automatically for PNG and NOT managed for SVG.
If it is already managed automatically for PNG (these images preview.url are PNGs) then why was it blurred? The original PNG image is of size 64x64 pixels.
Why did images become bigger after I've multiplied sourceSize by devicePixelRatio?
Addition #1. I'm using data:// scheme in images' URLs. I.e.
QString url("data:");
url += imageMimeType;
url += ";base64,";
url += imageData.toBase64();
While referring to the other answer I gave you here, my understanding of the Qt code is that, outside from the cases described here, Qt does no scale that value. So my understanding seems to be the opposite of what you state in 1. What evidences led you to that statement?
Assuming this concept is correct (I may be incorrect), the reason for point 2, would be that Qt is loading a size that would be appropriate for 1x scaling. But for higher factors, there is a quality loss. If you are on retina, then you should adjust that value, or provide the #2x version.
As for point 3, the reason is probably in the docs: "if the width and height properties are not specified, the Image automatically uses the size of the loaded image". So if you double the size of the loaded image, you double the size of the element, as you are not setting width and height explicitly.
Setting width and height solves the problem because the image is loaded in full size, so it can draw with proper quality after the scaling.
Hope someone else can correct me if my understanding of Qt code is wrong.

QML/Qt: Make displayed text as large as possible depending on the parent containing it

I am trying to make a very simple KDE-Plasma Widget where only a certain number is displayed. I want to make this displayed number have a font size as large as possible depending on the parent containing it.
Here is what it looks right now:
As you can see, the text inside has a lot of space around it. What I actually want it to be is something like the "Date And Time" Widget found in KDE Plasma (my widget is right next to it for comparison):
Here, the time displayed has much lesser space around it while also auto-resizing whenever the panel height is changed.
Here is what the current code looks like:
import QtQuick 2.6
import QtQuick.Layouts 1.0
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.plasmoid 2.0
Item {
id: main
anchors.fill: parent
Layout.minimumWidth: units.iconSizes.large
Layout.minimumHeight: units.iconSizes.large
Plasmoid.preferredRepresentation: Plasmoid.fullRepresentation
PlasmaComponents.Label {
id: display
anchors {
fill: parent
margins: Math.round(parent.width * 0.1)
}
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: foobar
font.pixelSize: 1000;
minimumPointSize: theme.smallestFont.pointSize
fontSizeMode: Text.Fit
font.bold: true
}
Timer {
some stuff
}
}
I tried looking into the code of the above Date and Time widget and wrote down the exact same layouts/controls (which is what you are seeing in the above code) with the same positioning/styling properties and yet I get a lot of space around my text/or the font size continues to remain small.
I tried your code and it resized the font correctly. For the spacing around the text, there are two points:
The spacing on the left and right is easily controlled by adjusting the margins value that you are using. For less space, try Math.round(parent.width * 0.05).
The spacing on the top and bottom is larger because the shape of your parent object is square, while the shape of the text is rectangular. In order to make the text fit the height of the square without exceeding the width of the square, the text would not just need to resize, it would need to be stretched vertically. But QML does not have an easy way to do that, and I doubt that's really what you want anyway.
EDIT:
And if you do want font stretching, I'll point you to this answer.
Thanks to #JarMan's input I was able to realize that my text was being rendered in small font because of lack of space due to the root (item) element being square in shape.
I have now figured that to change the layout sizes of the root element inside the KDE-Plasma panel, one needs to mess with Layout.preferredWidth and Layout.preferredHeight.
Here is what I did:
item {
.
.
Layout.preferredWidth: 150 * units.devicePixelRatio
Layout.preferredHeight: 50 * units.devicePixelRatio
.
.
}
Note: the 150 and 50 values aren't final. It basically gives an idea about the ratio at which the root element's width and height should be in (and I wanted a rectangle). It automatically resizes the inner content too as the Plasma Panel is resized.

How to create a square Button in Qt QML?

I'm trying to create a simple square-shaped button in QML, but it is not working as expected. Here is the minimum amount of code:
import QtQuick 2.7
import QtQuick.Controls 2.1
Page {
id: app
width: 400
height: 640
Button {
width: 48
height: 48
}
}
Which produces this result:
Any idea what is going on? I've looked through the docs and tried to set the implicitWidth and implicitHeight through a defined background item, but that blocks the built in hover and shadow functionalities of the Button.
Button {
background: Rectangle {
implicitWidth: 48
implicitHeight: 48
color: "gray"
}
}
results in:
Are there any ways to set the desired size without explicitly defining a background item?
Edit:
I've also tried setting all padding to 0 to see if padding is an issue:
Button {
width: 48
height: 48
topPadding: 0
bottomPadding: 0
leftPadding: 0
rightPadding: 0
}
As well as following a colleague's advice to add a text element to see where it is placed:
Button {
width: 48
height: 48
Text {
text: "hello"
}
}
The text placement suggests to me that the button may extend past the top bounds of the visible gray rectangle. Could this be the case?
Another note: The emulator I am using is the AppStudio for ArcGIS emulator installed in Qt Creator. Qt 5.10.0, Windows 10.
Any idea what is going on? I've looked through the docs and tried to set the implicitWidth and implicitHeight through a defined background item, but that blocks the built in hover and shadow functionalities of the Button.
The Material style Button has some padding which is probably what's causing it to not be square. Setting each padding property to 0 (or equal values) should help:
leftPadding: 0
rightPadding: 0
topPadding: 0
bottomPadding: 0
Mitch was on the right track that the Qml Material style Button has some padding but the background Rectangle also has this line:
height: parent.height - 12
which is a bit troublesome. You could make a hacky solution by setting your button height 12 more than the width (and also changing the padding) but I would not suggest it.
Instead like they suggest with Customizing Qt Quick Controls I would make MyButton.qml and copy the Material style Button.qml code there and just change the background height and the control padding. This makes it easier to do more style changes later since you do not need to change all the Buttons in your application.

How to make svg icons crisp again in Qt 5.6 on high DPI screens

Upgrading from Qt 5.4 to Qt 5.6 made all my .svg icons blurry/pixelated. I noticed this happens only on high density screens like on my MacBookPro Retina Display. I read the documentation of High DPI support in Qt 5.6 and I have set the QT_AUTO_SCREEN_SCALE_FACTOR=1 environment variable, but it didn't have much effect. Anybody has this issue? I have also found this bug report which probably relates to my question.
EDIT 1:
A simple example would be:
Image {
source: my_icon.svg
sourceSize.width: 50
sourceSize.height: 50
anchor.centerIn: parent
}
This is an ugly hack, but it did the trick:
Item {
property alias image: mySvgImage
implicitWidth: mySvgImage.paintedWidth
implicitHeight: mySvgImage.implicitHeight / Screen.devicePixelRatio
Image {
id: mySvgImage
sourceSize.width: width * Screen.devicePixelRatio
sourceSize.height: height * Screen.devicePixelRatio
}
}
I'm not sure how to apply this in QML, but you need to set the attribute AA_UseHighDpiPixmaps with QWidgets. Might well be the same with QML. E.g:
app.setAttribute(Qt.AA_UseHighDpiPixmaps)

QML: set smooth painting

While playing with QML I noticed a strange behavior. Let's say I have simple QML code:
Rectangle {
anchors.centerIn: parent
width:200
height:100
color:"yellow"
border.color: "green"
border.width: 10
rotation: 190
}
And the result:
The image looks poor, especially the border.
But if I add radius: 1 I get a very different picture:
Now it looks much better. the border also looks more smoother. Btw, setting smooth property do nothing. It looks that setting radius switches on some internal smoothing. So my question - how can I set this smooth without setting radius?
I use Qt 5.4 on Debian 7
The property you need to set is antialiasing. This is documented here:
antialiasing : bool
Used to decide if the Rectangle should use antialiasing or not.
Antialiasing provides information on the performance implications of
this property.
The default is true for Rectangles with a radius, and false otherwise.
(Emphasis mine.)

Resources