SVG
TouchGFXバージョン4.21から、SVG画像の使用がサポートされています。 SVG画像は、従来のビットマップ・ベースのグラフィックスと組み合わせて、ベクタ・グラフィックを使用したユーザ・インタフェースを作成するために使用できます。
SVG画像はSVGImageウィジェットを通してユーザ・インタフェースに含まれます。このウィジェットは、TouchGFX Designerで使用でき、他のウィジェットと同じようにユーザ・インタフェースに追加できます。
SVG画像は、16bpp、24bpp、または32bppのフレームバッファのみでサポートされます。
SVGとは
スケーラブル・ベクタ・グラフィックス(SVG)は、グラフィックプリミティブの小セットを使用して2D画像を定義するXMLベースのファイル・フォーマットです。プリミティブには長方形、円、曲線などがあります。プリミティブは色やグラデーションで塗りつぶすことができます。アウトライン(ストローク)を特定の幅で描くこともできます。
SVGグラフィックスの第一の重要な機能はスケーラブルであることです。つまり、指定した描画が1つの解像度で使用できるだけでなく、画質を落とさずに拡張 / 縮小可能であることです。これは、拡大すると視覚的な乱れが生じ縮小すると細部が失われるビットマップと比べて、大きな利点になります。
SVGグラフィックスの2つ目に重要な機能は、ベクタ・ベースであることです。つまり、画像はラインや円などの一連の幾何学図形で構成され、その組み合わせによって描画が作成されます。基本的に画像内のすべてのピクセルの色の値を定義するビットマップ画像とは対照的です。ベクタ定義の利点は、ほとんどの場合に画像がビットマップと比べて非常に小さく、柔軟性が非常に高いことです。たとえば、数個の黄色の楕円が表示されている画像を、緑色の楕円で構成される画像に簡単に変更できます。
TouchGFXは、SVG Tiny 1.2のサブセットをサポートしています。 TouchGFXのハードウェア、ランタイム環境、パフォーマンス特性によって生じる制限内では、すべての仕様をサポートすることはできません。
SVG画像は、多くの画像作成ツールで作成することができます。あるいは手書きで作成することもできます。独自のSVGを作成する方法を習得したい場合は、こちらの概要を参照することをお勧めします。
TouchGFXにおけるSVGの使用
SVG画像はPNG画像と同じように使用できます。プロジェクトで使用するSVGファイルは、assets/imagesフォルダに置く必要があります。 TouchGFX Image ConverterとTouchGFX Designerは、このフォルダにある画像を読み出します。
SVG画像はCPPコードに変換され、アプリケーションにリンクされます。このCPPコードは、generated/images/src/SVGDatabase.cppファイルに保存されます。
SVG画像はSVGImageウィジェットで使用されます。コードまたはTouchGFX Designerを用います。このウィジェットでは、SVG画像のスケーリング、変換、回転ができます。
SVGImageウィジェットは別のコンポーネントであるVectorRendererを使用して描画を行います。
例
ここでは例として、次のSVGを使用します。
<?xml version="1.0" encoding="iso-8859-1"?>
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
<circle cx="50" cy="50" r="40" fill="green" />
<polygon points="50,10 78,78 22,78" fill="gold" />
</svg>
このSVGは半径が40の深緑色の円と、黄色(金色)の三角形で構成されます。
ご覧のように、SVG画像は単純なテキスト・ファイルなので、通常のテキスト・エディタで変更できます。 SVGファイルはさまざまな標準ツールで開くことができます。 SVGビューアの一例としてChromeブラウザがあります。
このSVGの描画は次のようになります。
このSVGをTouchGFXで使用するには、このファイル(arrow.svg)を自分のプロジェクトのassets/imagesディレクトリに置く必要があります。これはTouchGFX Designerで使用するPNGファイルや画像と同じディレクトリです。
TouchGFX Designerで、SVGImageウィジェットを挿入できます。
右側のSVGImageのプロパティを使用して、ウィジェットのサイズとスケールを変更できます。
[Fit Image To Size]チェック・ボックスを確認します。オンにした場合、SVGはウィジェットのサイズに合わせてスケーリングされます(回転はリセットされます)。これは補助機能で、適切なサイズまでウィジェットの隅をドラッグすることでSVGのサイズを変更できます。
上の図のような回転を行うには、回転の中心を設定することが重要です。デフォルト値はx=0、y=0で、ウィジェットの左上隅になります。この回転中心を使用してSVGを回転した場合は、下の図に示すように、SVGImageウィジェットの左に向かって時計回りに回転します。
上の図に示すように、SVGImageの外にはみ出た部分のSVGはクリッピングされます(表示されません)。グレーの四角形はSVGImageを表しています。グレーの領域の外にある部分は何も描画されません。 SVGImageの内部でSVGを回転させるには、回転中心をウィジェットの中心(50, 50)に設定する必要があります。
変換の詳細については、後のセクションも参照してください。
ターゲット設定
ターゲット上でSVG画像を使用するには、STM32CubeMXでベクタ・レンダリングを有効にする必要があります。これにより、SVGImageで必要なVectorRendererを作成するために必要なコードがプロジェクトに挿入されます。
Note
ベクタ・レンダリングが有効でない場合、プロジェクトのビルド時にリンカ・エラー(undefined reference to `touchgfx::VectorRenderer::getInstance())が表示されます。
このエラーは、STM32CubeMXで設定を変更すると解決します。 Generatorユーザガイドも参照してください。
SVG画像はターゲット・プロジェクトにC++コードとして自動的に組み込まれるので、それ以上のアクションは必要ありません。
SVG画像の描画時間はビットマップよりも大幅に長くなる可能性があるので、ダブル・バッファリング(または、GRAM内蔵ディスプレイでのシングル・バッファリング)の使用をお勧めします。シングル・フレームバッファも使用できますが、描画時間とディスプレイのティアリングを監視する必要があります。フレームバッファの使用の詳細については、こちらを参照してください。
キャンバス・バッファ
ほとんどのプラットフォーム(U5X9を除く)で、SVGはキャンバス・ウィジェット・レンダラを使用して描画されます。このソフトウェア・コンポーネントは描画時にバッファを使用します。このバッファのサイズがパフォーマンスに影響します。可能な場合は、バッファのサイズを増やすことを推奨します(20~30KBが適していることが多い)。バッファはTouchGFX Designerで簡単に変更できます。
関連するすべてのスクリーンで値を変更することを忘れないでください。
ターゲットのパフォーマンスとFlashの使用量
矢印用に生成されたSVGデータのサイズは350バイト未満です。このことは、ビットマップと比べてSVGを使用するメリットの1つを示しています。 100x100のサイズの矢印をビットマップで作成すると、データのサイズは24ビット・カラーで30,000バイトになります。つまり、SVGのデータ・サイズはビットマップの1%程度だということです。
| 画像タイプ | Flash使用量 |
|---|---|
| ビットマップ | 30,000バイト |
| SVG | 350バイト |
SVG画像を使用すると、アプリケーションに5~10KBのコードが追加されます。
SVG使用のマイナス面はSVG画像の描画パフォーマンスで、ほとんどの場合ビットマップよりも低くなります。 SVGの描画はSVGの要素数に応じて決まり、これには上限がありません。ビットマップの描画は解像度とアルファ・ブレンディングに依存し、上限があります。 2つ目に、SVG画像の描画にはソフトウェア・レンダリングを多用されるので、CPUの負荷が高くなります。一方でビットマップは、DMA2Dアクセラレータによって排他的に描画できるので、CPU負荷が低くて済みます。
SVG矢印の描画時間はSTM32F746(外部RAMにフレームバッファ)では1.32msで、これは非常に有効です。 H7シリーズの高速マイクロコントローラでは、描画時間はもっと短縮されます。 SVGを拡大すると(描画するピクセル数が増えるので)描画時間は増えますが、回転の場合はそれほど増えません。次の図は、STM32F746 Discovery Kitでの矢印を示しています。
一番左の画像にはSVGはなく、緑色のボックスのバックグラウンドが表示されているだけです。中央にはスケール1の矢印が示されています。右端にはスケール2の矢印が表示されています。フレーム全体とSVGのみの描画時間を以下に示します。
| スクリーンの | 描画時間 | SVGのみ |
|---|---|---|
| 左 | 2.96 ms | N/A |
| 中央 | 4.28 ms | 1.32 ms |
| 右 | 6.37 ms | 3.41 ms |
| 回転 | 6.53 ms | 3.57 ms |
最後の行は、矢印を77度回転させたときの描画時間を示しています。回転させても描画時間にはさほど影響しないことがわかります。
グラフィックスの品質
下の画像は、STM32F746 Discovery Kitで実行中のプロジェクトを示しています。
このプロジェクトでは、複数のSVGImageウィジェットで同じSVGを使用しました。
正しいフォトサイズで、フレームバッファからズームされた詳細部分を挿入しました(3つの矢印のヘッド)。これにより、回転やスケーリングを行っても、SVG描画のラインがすべてアンチエイリアスされていることがわかります。これがSVG画像を使用する最大の理由です。つまり、回転やスケーリングを行っても品質が保たれるのです。
Storing SVG images in external flash
All SVG data for an application is generated into SVGDatabase.o. To store this data in external flash, add an output section to the linker script and map it to the external flash memory region. For example (for gcc):
ExtFlashSection :
{
*/SVGDatabase.o(.rodata.*)
} >EXT_FLASH
This moves all read-only data from SVGDatabase.o into EXT_FLASH. Storing SVG data in external flash may slightly reduce performance, but can be useful in applications that contain many SVG images to save internal flash.
サポートされているSVG要素
TouchGFXは、SVG Tiny 1.2標準を完全実装していません。サポートされていない要素や属性を使用すると、.svgファイルを読み出すTouchGFX Image Converterが警告を発します。サポートされていない重要な部分は、放射状グラデーション、テキスト、アニメーションです。
TouchGFXは以下のSVG要素をサポートしています。以下の例は、サポートされている要素の属性を示しています。
長方形 <rect x="10" y="20" width="100" height="200" rx="5" ry="40" />
円 <circle cx="100" cy="200" r="20" />
楕円 <ellipse cx="100" cy="200" rx="20" ry="30" />
ライン <line x1="10" y1="10" x2="300" y2="150 />
パス <path d="M100 100L75 200h50z" />
ポリゴン <polygon points="10,10 30,30 10,90" />
ポリライン <polyline points="10,10 30,30 10,90" />
線形グラデーション <lineargradient id="grad1" gradientUnits="objectBoundingBox" x1="0%" y1="0%" x2="100%" y2="100%" />
以下の構造要素もサポートされています。
- グループ <g />
- 定義 <defs />
TouchGFXはすべての形状変換をサポートします(matrix、translate、scale、rotate、skewX、skewY)。
線形グラデーションは<defs>要素内で定義する必要があります。
<path>要素に対して、TouchGFXはすべてのパス・コマンドをサポートします(M、m、L、l、H、h、V、v、C、c、S、s、Q、q、T、t、A、a)。
フィルとストロークもサポートされます。
サポートされないSVG要素
TouchGFXは以下の要素をサポートしていません。
- アニメーション <animate>、<animateColor>、<animateMotion>、<animateTransform>、<discard>、<handler>、<listener>、<mpath>、<script>、<set>、<switch>
- オーディオ <audio />
- クリッピングパス <clipPath id=.... />
- ディスクリプション <desc />
- フィルタ <filter>
- Foreignオブジェクト <foreignObject>
- グラデーション変換 <linearGradient gradientTransform="..." />, <prefetch>
- 放射状グラデーション <radialGradient ... />
- ソリッド・カラー(塗りつぶし色) <solidColor>
- Stroke dasharrayプロパティ <path stroke-dasharray="10,10" ... />
- テキストとフォント <font>、<glyph>、<text>、<hkern>、<missing-glyph>、<tbreak>、<textArea>、<tspan>
- 使用 <use>
- 画像 <image>
- ビデオ <video>
メタデータとタイトル要素は無視されます。
ViewBox
viewBox属性はSVG画像でよく使用されますが、TouchGFXではサポートされません。 viewBox属性は、SVGの描画で使用された座標系(ユーザ領域)から、SVGが描画されるビューポートへの変換を定義するために使用されます。
次に例を示します。
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200">
...
</svg>
これは、このSVGによって(0,0)から(200,200)の長方形に描画されたグラフィックスは、ビュー(SVGImageウィジェット)にフィットするようにストレッチ(またはシュリンク)する必要があることを意味しています。
TouchGFXでは、viewBox属性は無視されます。つまり、グラフィックスはスケーリングされません。多くのSVG画像では、グラフィックスが正しいスケールで描画されるので、このことは問題にはなりません。その場合には、viewBox属性はそのままにしておいても削除しても構いません。グラフィックスもviewBoxではなく、SVGImageウィジェットに従ってクリップされます。
特定の描画のスケーリングにviewBoxが実際に必要になった場合は、追加の<g>要素などで変換を挿入することでSVGファイルにスケーリングを適用するか、TouchGFXで描画をスケーリングできます。
次の例では、viewBoxによって、(0,0)から(1000,1000)のラインが(0,0)から(100,100)に縮小されています(これは動作しません)。
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 1000 1000">
<path d="M10 10L990,990" stroke="blue" stroke-width="10" stroke-linecap="round"/>
</svg>
(意図した)結果は(0,0)から(200,200)のラインです。ところが、TouchGFXがViewBoxを無視するため、得られるのは1000x1000ピクセルのSVG画像です。これを変換する1つの方法は、スケール変換を含むようにSVGファイルを変更することです。
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
<g transform="scale(0.2)">
<path d="M10 10L990,990" stroke="blue" stroke-width="10" stroke-linecap="round"/>
</g>
</svg>
この変換では、すべてが1/5にスケールダウン(縮小)されます。
SVGImageウィジェットにスケーリングを適用してもこれと同じ効果を得られます。
アプリケーションで何らかのスケーリングを行う場合、.svgファイルを変更しない方が良いかもしれません。
線形グラデーションではObjectBoundingBoxを使用すること
SVGの線形グラデーションは、2つの異なる座標系を使用して指定できます。 "userSpaceOnUse"または"objectBoundingBox"です。 TouchGFXは"objectBoundingBox"のみをサポートしています。ここでは、"objectBoundingBox"の使用方法について簡単に説明し、"userSpaceOnUse"を使用しているSVGが"objectBoundingBox"を使用するように変更できるようにします。まず、次のサンプルから開始します。
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="100">
<defs>
<linearGradient id="gradient1" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="blue" />
<stop offset="50%" stop-color="green" />
<stop offset="100%" stop-color="red" />
</linearGradient>
</defs>
<circle cx="50" cy="50" r="40" fill="url(#gradient1)" />
<rect x="130" y="10" width="40" height="80" fill="url(#gradient1)" />
</svg>
これは、青色から始まり、中央で緑色になり、最後は赤色へと遷移するグラデーションを定義しています。このグラデーションは円や長方形を塗りつぶすために使用されます。
グラデーションの始点は(x1, y1) = (0%, 0%)と指定されます。これは、このグラデーションでペイントされる任意の形状(円や長方形など)の境界ボックスの左上隅を指しています。同様に、終点は境界ボックスの右下隅になります。
この円と長方形の2つの形状は幅が異なるので(80ピクセルと40ピクセル)、形状への適用時のグラデーションの角度も異なります。この動作が好ましくない場合は、複数のグラデーションを定義できます。各形状に1つずつです。
SVGにおける別の可能性として、"userSpaceOnUse"座標系の使用が考えられます。
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="100">
<defs>
<linearGradient id="gradient1" gradientUnits="userSpaceOnUse" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="blue" />
<stop offset="50%" stop-color="green" />
<stop offset="100%" stop-color="red" />
</linearGradient>
</defs>
<circle cx="50" cy="50" r="40" fill="url(#gradient1)" />
<rect x="130" y="10" width="40" height="80" fill="url(#gradient1)" />
</svg>
これにより異なるレンダリング結果がもたらされます(TouchGFXではサポートされません)。
これが違うもののように見えるのは、両方の形状のグラデーションが、(両方の形状が含まれる)画像全体の左上隅を始点にしているからです。
userSpaceOnUseを使用するSVGがある場合は、グラデーションを使用する形状を基準にしてグラデーションの始点を計算することで、objectBoundingBoxを使用するように変換できます。次の例では、ピクセル座標のuserSpaceOnUseを使用して、円をグラデーションで色付けしています。
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="100">
<defs>
<linearGradient id="gradient1" gradientUnits="userSpaceOnUse" x1="10" y1="10" x2="90" y2="90">
<stop offset="0%" stop-color="blue" />
<stop offset="50%" stop-color="green" />
<stop offset="100%" stop-color="red" />
</linearGradient>
</defs>
<circle cx="50" cy="50" r="40" fill="url(#gradient1)" />
</svg>
円の境界ボックスを計算することで、グラデーションがobjectBoundingBoxを使用するように変換できます。この値は(10,10)から(90,90)で、これがグラデーションと一致しています。したがって、"10"を"0%"に、"90"を"100%"に置き換えることで、座標を相対的な形状座標に変換できます。
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="100">
<defs>
<linearGradient id="gradient1" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="blue" />
<stop offset="50%" stop-color="green" />
<stop offset="100%" stop-color="red" />
</linearGradient>
</defs>
<circle cx="50" cy="50" r="40" fill="url(#gradient1)" />
</svg>
"%"を忘れないでください。 y1="10"はy1="1000%"を意味します。
Image Converterの警告
サポートされない要素や属性を使用すると、TouchGFX Image Converterが警告またはエラーを出力します。例として、矢印にテキストを配置してみます。
<?xml version="1.0" encoding="iso-8859-1"?>
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
<circle cx="50" cy="50" r="40" fill="green" />
<polygon points="50,10 78,78 22,78" fill="gold" />
<text x="38" y="78">ABC</text>
</svg>
TouchGFX Designerが.svgファイル上でImage Converterを実行し、次のエラーが表示されます。
エラー出力には、エラーが含まれるSVGファイル、問題の見つかった行、使用されたサポートされない要素が表示されます。
関連記事
前述のように、ターゲット・プロジェクトに対するベクタ・グラフィックスのサポートはTouchGFX Generatorで設定します。手順については、Generatorユーザガイドを参照してください。
SVGImageウィジェットはTouchGFX Designerで利用可能です。 TouchGFX DesignerでのSVGImageウィジェットの使用方法については、こちらをご覧ください。












