Qt 6.8 のベクターグラフィックス

本稿は「Vector Graphics in Qt 6.8」の抄訳です。
 

最近の Qt リリースノートでは、2 次元のベクターグラフィックスがよく使われています。このブログでは、Qt 開発者としての選択肢のいくつかを紹介します。

Qt 6.6 では、Qt Quick Shapes新しいレンダラーのサポートを追加し、マルチサンプリングを有効にしなくても、滑らかでアンチエイリアスな曲線をレンダリングできるようにしました。このレンダラーは Qt 6.7 でテキストレンダリングもサポートするように一般化され、同じリリースでQt SVG が拡張され、多くの新機能がサポートされました。

そしてまだ終わりは見えません。Qt 6.8 では、Qt API にさらに多くのベクターグラフィックス機能を追加する予定です。このブログでは、Qt でベクターグラフィックスを使う様々な方法と、それぞれの利点と欠点について詳しく説明します。

QPainter、Qt SVG、Imageコンポーネント

Qt Widgetsモジュールの中核にはQPainterがあります。これはスタンドアロンの C++ API で、2D ベクターグラフィックスをピクセルパーフェクトアンチエイリアスでレンダリングするために使用できます。これだけで、アプリケーションはQPainterPaths で記述された任意の図形や曲線だけでなく、矩形、楕円、テキストなどのプリミティブを描画することができます。

Qt SVG モジュールはこの上に位置し、複雑な SVG ファイルの解析とQPainter を使った描画をサポートします。ベースラインとして、Qt SVG は SVG Tiny 1.2 プロファイルの静的な部分をサポートしています。Qt 6.7 以降では、フィルタマスクなど、完全な SVG プロファイルの追加機能もサポートしています。

Qt SVG とQPainterは、Qt アプリケーションで 2D ベクターグラフィックスをレンダリングするための強力なツールです。

Decorative Angel Polyprismatic Pattern by OpenClipart rendered with Qt SVG

上図は、曲線やグラデーションの多い SVG 画像の例として、OpenClipart で作成されたパブリックドメインのプリズムエンジェル SVGをレンダリングするために Qt SVG を使用した SVG Viewer の例です。

Qt SVG は主に C++ API を持っていますが、そのレンダラーは画像フォーマットプラグインを通して公開されています。つまり、Qt で画像を読み込める場所であればどこでも、SVG ファイルを読み込むことができます。そのような場所の一つが Qt Quick のImageコンポーネントです。これを使うことで、Qt QuickアプリケーションでSVGをレンダリングすることができます。

QPainter特徴として、ソフトウェアラスタライザであるため、ペイントコマンドはすべてCPU上で実行されます。Qt Quick から使用する場合、画像はまずソフトウェアで作成され、その後 GPU にアップロードされ、Qt Quick の他のシーンと一緒に表示されます。

これにはいくつかの注意点があります。ペイントコマンド自体にハードウェアアクセラレーションがないため、画像のレンダリングに時間がかかることがあります。また、レンダリングが完了すると、レンダリングされた画像は GPU メモリにアップロードする必要があります。これは特定のハードウェアでは遅いことがあります。ラスタライズされた画像も、要求されたサイズでメモリに保存されます。これは、画像をさらに拡大するには、より大きな縮尺で再レンダリングする必要があることと、ターゲットサイズが大きくなるにつれてメモリ消費量が増えることを意味します。

とはいえ、SVG画像のレンダリングが完了し、GPUメモリに保存されると、画面へのBLIT処理は非常に高速になります。これは実際、ビルド時にSVGをPNGファイル(または同様のもの)にプリレンダリングし、代わりにこのプリラスタライズされた画像を表示するのと同じように実行されます。SVGをプリレンダリングするのが一般的に最もパフォーマンスの高いオプションですが、ビルド時にターゲットサイズを知っている必要があり、例えばターゲットサイズが画面の解像度に依存する場合など、常に可能とは限りません。

そのため、画像が特定のサイズで一度レンダリングされ、その後ほとんど更新されないような状況では、Qt SVG は柔軟でパフォーマンスの高い選択肢となります。

Qt Quick Shapes、VectorImage、svgtoqml

Qt Quick Shapesは Qt Quick で任意の曲線や図形を表示するためのモジュールで、QPainterQPainterPathを使って図形を描くのに似ています。現在までは、Qt Quick Shapes は曲線や直線、さらにはSVG のパス記述から手動でシェイプを作成することをサポートしていますが、サードパーティのアプリケーションで作成されたファイルを読み込むことはサポートしていませんでした。

Qt 6.8 では、このギャップを埋めるために新しいVectorImage型を導入します。VectorImageコンポーネントは SVG ファイルを読み込むことができ、Qt Quick に既に存在する型を使用してレンダリングします。これは、Qt SVGがSVGファイルを解析し、QPainterコマンドのシーケンスに変換する方法に似ています。QPainterコマンドの代わりに、VectorImageはSVGを表すQt Quickアイテムのシーンを作成します。例えば、SVGにテキスト要素が含まれている場合、VectorImageはその場所にTextコンポーネントを作成します。また、Qt Quick Shapesモジュールが図形や曲線のサポートに使われます。

VectorImageは Qt Quick シーンを構築するので、形状のジオメトリは最初にラスタライズされた画像に描画されるのではなく、GPU メモリに直接アップロードされます。ターゲットサイズが大きい場合、これはより少ないメモリを消費することができますが、より重要なことは、曲線のラスタライズとアンチエイリアシングがハードウェアで加速され、シーンが画面にレンダリングされるときに行われることを意味します。

前述のように、単純なImageコンポーネントを使用することは、指定されたサイズでラスタライズすることを意味します。シーンを描画するときにこの結果を拡縮しようとすると、レンダラがアクセスできるのはラスタライズ済みのピクセルだけで、実際の曲線データではないため、不鮮明な結果になることがあります。

SVG rendered with QPainter and then scaled to show upscaling artifacts

上図は先ほどのプリズムエンジェルの拡大図です。スムーズピックスマップフィルタリングが適用され、画像の細部が失われています。この画像を高品質にレンダリングするには、ImageコンポーネントのsourceSizeプロパティを更新する必要があります。そうすることで、Qt SVG は要求されたサイズで画像の新しいレンダリングを作成します。倍率が変わるたびに再レンダリングするのは非常に時間がかかりますし、例えば、画像のアニメーションズームを行うことは多くの場合、論外です。

代わりに、VectorImageコンポーネントが形状を三角形に分割し、その情報をGPU上に保持します。画像が拡縮されると、単に均一行列が更新され、フラグメントシェーダーがカーブを希望するレベルの詳細度でレンダリングします。

Same SVG rendered with VectorImage and scaled, showing that details are retained

この図では、同じ画像をVectorImageでレンダリングされています。曲率に関する情報はシェーダプログラムによって知られているので、シェーダプログラムはディテールをそのままに変換された画像を生成することができます。

トレードオフは、シーンのジオメトリとフラグメントシェーダの両方が、単にテクスチャ付きの四角形として画像を表示する場合よりも複雑になるということです。そのため、単一のサイズでしかレンダリングされない画像には、Imageコンポーネントを使用するのが正しい選択であることがよくあります。

Qt 6.8には、「svgtoqml」という新しいツールも搭載されています(Qt 6.7では技術プレビュー)。これは、VectorImageと基本的に同じ目的を果たしますが、SVGを事前にQMLファイルに変換するために使用できます。SVGがアプリケーションリソースの一部である場合、実行時に画像を解析および変換するよりも、この方法の方が高速にロードできます。

その他

このブログでは、主にSVGと、最近のリリースでQtに追加された機能について取り上げてきました。最後に、Qtで使用できる他のベクターグラフィック形式についても少し触れておきたいと思います。

1つは、Qt Lottie AnimationモジュールによってQtでサポートされているLottie形式です。これは、AirBnBの開発者によって作成されたJSONベースの形式です。一般的なベクターグラフィック形式ですが、もともとの主な目的はAdobe After Effectsで作成されたアニメーションをサポートすることでした。Qt SVGはアニメーションのサポートが非常に限られているため、Qt Lottie Animationは、アプリケーションがアニメーション付きのベクターアイコンを表示するための方法となります。

Qtでの実装には一定の制限があり(ドキュメントの概要を参照)、当初作成されてから新しい機能をサポートするために更新されていません。つまり、一部のLottieファイルは作成者の意図したとおりに動作しないということです。しかし、このことについてのご意見や、今後追加してほしい具体的なサポートについてのご意見をお待ちしております。優先すべき事項についてご意見をお持ちの場合は、Qtのバグトラッカーに提案を提出してください。

技術的な面では、Qt Lottie AnimationはQt SVGと似た動作をします。つまり、 QPainterを使用してソフトウェアでベクター画像をレンダリングします。今のところ、Lottie形式のVectorImageに相当するものはありません。

最近注目を集めているもう一つの形式はRiveです。これもアニメーション化されたベクターグラフィック形式で、独自のオープンソースランタイムが付属しています。basysKomによるプロジェクトでは、このランタイムをQtのRHI と組み合わせ、Qt Quickで使用できるハードウェアアクセラレーションレンダラーを作成しています。これはQtの公式サポート対象ではなく、basysKomがLGPLv3ライセンスでリリースしていることに注意してください。

そして最後に、個人的に最も好きな話題、フォントについてです。主にテキストの表示に使用されるTrueType(およびOpenType)フォントファイルフォーマットですが、それ自体がベクターグラフィックフォーマットでもあります。ほとんどのフォントはモノクロですが、多色ベクターフォントフォーマットもあり、両者はベクターアイコンの表示に頻繁に使用されます。アイコンフォントの例としては、人気の高い Font Awesomeや、Microsoftの古典的なWingdingsがあります。また、最近登場した「可変フォント」により、限定的ではありますが、アイコンの特定の要素をアニメーション化することも可能になりました。Qtは、システムライブラリとFreetypeを介してフォントをサポートしています。フォントのサポートはテキスト用に最適化されていますが、フォントレンダラーは通常システム上に常駐しており、追加のライブラリやレンダラーを必要としないため、ベクターグラフィックに再利用することは有用です。


Blog Topics:

Comments