通常、Skiaで描画を行う場合、ウィンドウやビューに直接描画することが多いです。しかし、見えないメモリ上で描画を行い、その結果を後で画面に表示したり、画像として保存したりすることもよくあります。これを「オフスクリーン描画」と呼びます。
ここでは、Skiaでのオフスクリーン描画の基本的な手順について説明します。
そもそもどこに描画するのか?
Skiaで描画を行うためには、紙にあたる描画先を指定する必要があります。オフスクリーン描画の場合、通常は以下の2つの方法があります。
-
ビットマップ(SKBitmap)を使用する方法
SKBitmapは、ピクセルデータを格納するためのクラスです。キャンバスを用いた描画のほか、ピクセルデータへのバイト単位の直接アクセスも可能です。描画は必ずCPUで行われます。 -
サーフェス(SKSurface)を使用する方法
SKSurfaceは、描画先の抽象化されたクラスです。スナップショットなどの高レベルAPIを提供します。CPUだけでなく、GPUによる高速な描画もサポートしています。
| 機能 | ビットマップ(SKBitmap) | サーフェス(SKSurface) |
|---|---|---|
| 使いやすさ | △ | 〇 |
| 高レベルAPI | × | 〇 |
| GPUアクセラレーション | × | 〇 |
| ピクセルデータの直接アクセス | 〇 | × |
ビットマップを使用したオフスクリーン描画
ビットマップを使用してオフスクリーン描画を行う場合、以下の手順で進めます。
-
ビットマップの作成
描画先となるビットマップを作成します。using SKBitmap bitmap = new SKBitmap(width, height); -
キャンバスの作成
ビットマップ上に描画を行うためのキャンバスを作成します。このキャンバスは使い終わったら破棄する必要があります。using SKCanvas canvas = new SKCanvas(bitmap); -
描画の実行
作成したキャンバスに対して、いつも通りに描画を行います。
ピクセルデータの直接アクセス
オフスクリーン描画にビットマップを使用する場合、ピクセルデータへ直接アクセスすることができます。画像データをバイト列として取り出したり、OpenCVなどの他のライブラリと連携したりする際に便利です。
以下のコードは、ビットマップのピクセルデータの先頭ポインタと1行あたりのバイト数(ストライド)を取得する例です。
byte* ptr = (byte*)bitmap.GetPixels().ToPointer(); // SKBitmapの先頭
int stride = bitmap.RowBytes; // SKBitmap の1行のバイト数 (パディング含む)
サーフェスを使用したオフスクリーン描画
サーフェスを使用してオフスクリーン描画を行う場合、以下の手順で進めます。
-
サーフェスの作成
描画先となるサーフェスを作成します。SKImageInfo info = new SKImageInfo(width, height); using SKSurface surface = SKSurface.Create(info); -
キャンバスの取得
サーフェス上に描画を行うためのキャンバスを取得します。このキャンバスはサーフェスによって管理されるため、破棄してはいけません。SKCanvas canvas = surface.Canvas; -
描画の実行
取得したキャンバスに対して、いつも通りに描画を行います。 -
画像の取得
描画結果はサーフェスからスナップショットとして取得できます。このSKImageはイミュータブル(変更不可能)な画像データです。using SKImage image = surface.Snapshot();
GPUを使用したオフスクリーン描画
GPUアクセラレーションを利用したオフスクリーン描画を行う場合、以下の手順で進めます。
GPUを使用したオフスクリーン描画は、環境によっては正しく動作しない場合があります。特に、サーバー環境やヘッドレス環境ではOpenGLコンテキストの作成が失敗することがありますので注意してください。
-
追加のライブラリの導入
NuGetからOpenTKパッケージをインストールします。これにより、OpenGLを扱えるようになります。 -
グラフィックスコンテキストの作成
GPUアクセラレーションを利用するためのグラフィックスコンテキストを作成します。見えないウィンドウを作成して、それを描画先とします。var nativeWindowSettings = new NativeWindowSettings { ClientSize = new OpenTK.Mathematics.Vector2i( width, height ), WindowBorder = OpenTK.Windowing.Common.WindowBorder.Hidden, StartVisible = false }; using var window = new OpenTK.Windowing.Desktop.NativeWindow( nativeWindowSettings ); window.Context.MakeCurrent(); using var context = GRContext.CreateGl(); -
オフスクリーンサーフェスの作成
描画先となるオフスクリーンサーフェスを作成します。using var offscreenSurface = SKSurface.Create( context, false, new SKImageInfo( width, height ) ); -
キャンバスの取得
サーフェス上に描画を行うためのキャンバスを取得します。このキャンバスはサーフェスによって管理されるため、破棄してはいけません。SKCanvas canvas = offscreenSurface.Canvas; -
描画の実行
取得したキャンバスに対して、いつも通りに描画を行います。 -
画像の取得
描画結果はサーフェスからスナップショットとして取得できます。using SKImage image = surface.Snapshot();
まとめ
Skiaでのオフスクリーン描画は、ビットマップやサーフェスを使用して簡単に実現できます。用途に応じて適切な方法を選択しましょう。
総合目次