Qt Quick で SVG を表示する
Qt では QtSVG モジュールを利用して SVG ファイルを扱うことが可能です。
(SVG Tiny 1.2 の static feature をサポートしていて、それ以外の機能には対応していません。)
Qt Quick では、Image エレメントを利用して画像の表示が可能で、PNG や JPEG のようなラスタ画像や、SVG 形式のベクター画像、PKM や KTX、ASTC といった圧縮テクスチャに対応しています。
ということで、ベクター形式の SVG 形式の画像を Image エレメントに指定をすれば、どんな大きさでも綺麗に描画をしてくれる…といいのですが、少々工夫が必要です。
SVG を表示する際の注意点
現在の Qt の設計と実装により、PNG, JPEG, SVG などの画像は、一度 CPU で描画されたものが Qt Quick 側に渡ってきて GPU に送られ表示されます。
最初の CPU 描画の時点で描画する画像の大きさが決まってしまい、それを Qt Quick 側で GPU で Image の大きさに合わせて拡大縮小することになります。
つまり、SVG 形式の画像だからといって、Qt Quick で自由なサイズで綺麗に表示できるわけではなく、SVG の大きさ(<svg> エレメントの width と height アトリビュート、もしくは viewBox の大きさ)で1度ラスタライズされたものが Qt Quick 側に渡ってくるため、小さなサイズの SVG を拡大表示した際には画像の解像度の低さが目立ちます。
解決方法1
これを解決する簡単な方法は、CPU 側でラスタライズする際のサイズを Qt Quick から指定する方法で、これは Image の sourceSize を利用します。
実サイズを指定する場合は、
動的にサイズが変わる場合は、その度に SVG の画像化の処理が走りますので気をつけましょう。
解決方法2
Qt 5.10 で導入された Qt Quick の Shapes モジュール を利用することで、GPU でパスの描画に対応している場合には GPU での描画も可能になります。
簡単な SVG であれば、Shape エレメントに変換してしまいましょう。
(これを自動的に行うプログラム もあるようですが、うまく動作しませんでした)
ソースコード
今回書いたソースコードは https://github.com/task-jp/svg を参照してください。
import QtQuick.Layouts 1.15
import QtQuick.Window 2.15
import QtQuick.Shapes 1.15
Window {
id: window
width: 800
height: 200
visible: true
title: 'SVG'
GridLayout {
anchors.fill: parent
columns: 4
Text {
text: 'Original'
horizontalAlignment: Text.AlignHCenter
Layout.preferredWidth: window.width / 4
Layout.fillWidth: true
}
Text {
text: 'Filled'
horizontalAlignment: Text.AlignHCenter
Layout.preferredWidth: window.width / 4
Layout.fillWidth: true
}
Text {
text: 'Filled (with sourceSize set)'
horizontalAlignment: Text.AlignHCenter
Layout.preferredWidth: window.width / 4
Layout.fillWidth: true
}
Text {
text: 'PathSvg'
horizontalAlignment: Text.AlignHCenter
Layout.preferredWidth: window.width / 4
Layout.fillWidth: true
}
Item {
Layout.preferredWidth: window.width / 4
Layout.fillWidth: true
Layout.fillHeight: true
Image {
anchors.centerIn: parent
source: './triangle.svg'
}
}
Image {
Layout.preferredWidth: window.width / 4
Layout.fillWidth: true
Layout.fillHeight: true
source: './triangle.svg'
}
Image {
Layout.preferredWidth: window.width / 4
Layout.fillWidth: true
Layout.fillHeight: true
source: './triangle.svg'
sourceSize: Qt.size(width, height)
}
Shape {
id: svg
Layout.preferredWidth: window.width / 4
Layout.fillWidth: true
Layout.fillHeight: true
ShapePath {
fillColor: '#1B1E23'
startX: 15.3 * svg.width / 39.2
startY: 11.3 * svg.height / 39.2
PathLine {
x: 29.2 * svg.width / 39.2
y: 19.6 * svg.height / 39.2
}
PathLine {
x: 15.3 * svg.width / 39.2
y: 27.9 * svg.height / 39.2
}
}
}
}
}
<svg version="1.1"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
x="0px" y="0px" width="39.2px" height="39.2px" viewBox="0 0 39.2 39.2" style="enable-background:new 0 0 39.2 39.2;"
xml:space="preserve">
<style type="text/css">
.st1{fill:#1B1E23;}
</style>
<defs>
</defs>
<polygon class="st1" points="15.3,11.3 29.2,19.6 15.3,27.9 "/>
</svg>