雑記
 

C#によるLinuxでの画像描画 (SkiaSharp編)

2022/6/5

この記事が対象とする製品・バージョン

Visual Studio 2022 Visual Studio 2022 ◎ 対象です。
Visual Studio 2019 Visual Studio 2019 △ 対象外ですが、参考になります。
Visual Studio 2017 Visual Studio 2017 △ 対象外ですが、参考になります。
Visual Studio 2015 Visual Studio 2015 △ 対象外ですが、参考になります。
Visual Studio 2013 Visual Studio 2013 △ 対象外ですが、参考になります。
Visual Studio 2012 Visual Studio 2012 × 対象外です。
Visual Studio 2010 Visual Studio 2010 × 対象外です。
Visual Studio 2008 Visual Studio 2008 × 対象外です。
Visual Studio 2005 Visual Studio 2005 × 対象外です。
Visual Studio.NET 2003 Visual Studio.NET 2003 × 対象外です。
Visual Studio.NET 2002 Visual Studio.NET (2002) × 対象外です。
  Visual Studio Code △ 対象外ですが、参考になります。

 

目次

 

1.概要

Linux上で C#のプログラムを使って画像を生成する方法を紹介します。

VBでも通用するはずですが未確認です。

やり方はいくつかありますが、ここでは SkiaSharp を使用します。SkiaSharp はマイクソフト製のライブラリで、マイクロソフトもWindows以外の環境での画像描画にはSkiaSharpの利用を推奨しているようです。

なお、Windows上で便利に画像を描画できるSystem.Drawing名前空間の機能(通称 GDI+)はLinux上では使用できないか、使用が推奨されていません。

 

 

2.簡単な説明

2-1.プロジェクトの種類

Linuxで動作する種類のプロジェクトを使って開発することが前提です。たとえば、コンソールアプリケーションやASP.NET CoreはLinuxで動作するのでOKです。Windows フォーム アプリ や WPF アプリは Linux 上では動作しないので使用できません。

2-2.パッケージのインストール

次に、あなたのプロジェクトに NuGet から次の2つのパッケージをインストールします。

参考:NuGetでパッケージをインストールする方法

バージョンは私が試したものです。新しいものが出ている場合には、新しくても良いと思います。古いものはLinux上動作させるためにもうひと手間必要なことがあるようですので、これより古いバージョンは使わない方が良さそうです。

また、2つ目のパッケージは名前に「NoDependencies」とついていることに気を付けてください。これがついていない似た名前のパッケージがありますが、そちらではありません。

2-3.画像生成のプログラム

画像生成処理はお好みでOKです。たとえば、次のプログラムはグラデーションで塗りつぶされた三角形を描画し、pngファイルとして保存します。

using SkiaSharp;

//▼画像を生成
using SKBitmap bitmap = new SKBitmap(200, 200);

using SKPaint paint = new SKPaint();

paint.Shader = SKShader.CreateLinearGradient(
    new SKPoint(0, 0),
    new SKPoint(200, 200),
    new SKColor[] { SKColors.Red, SKColors.Blue },
    new float[] { 0, 1 },
    SKShaderTileMode.Repeat);

using SKCanvas canvas = new SKCanvas(bitmap);

SKPath path = new SKPath();
path.MoveTo(100, 0);
path.LineTo(200, 173);
path.LineTo(0, 173);
path.Close();

canvas.DrawPath(path, paint);

//▼PNG形式にする
var data = SKImage.FromBitmap(bitmap).Encode(SKEncodedImageFormat.Png, 100);
            
//▼ファイルに保存する
System.IO.File.WriteAllBytes("myskia.png", data.ToArray());

System.Diagnostics.Debug.WriteLine($"{Environment.CurrentDirectory} に画像を保存しました。");

 

3.ASP.NET Core の例

3-1.完成形

もうちょっと順を追って、サンプルプログラムを作成する方法も紹介しておきます。

ここでは、ASP.NET CoreのWebアプリケーションで SkiaSharpを使って描画した画像を表示するアプリケーションを作成します。Visual Studio 2022を使用しています。

 

3-2.プロジェクトの作成

次の要領でプロジェクトを作成してください。

C# で ASP.NET Core Webアプリを新規作成。

プロジェクト名 SkiaSharpOnLinux

フレームワークは .NET 6.0

HTTPS 用の構成のチェックをはずす。

 

3-3.SkiaSharpの組み込み

ソリューションエクスプローラーでプロジェクトを右クリックして、NuGet パッケージの管理から次の2つのパッケージをインストールします。

参考:NuGetでパッケージをインストールする方法

 

3-4.画像生成プログラム

ソリューションエクスプローラーでPagesフォルダーを右クリックして 追加 - Razor ページ。「Razor ページ - 空」を選択して追加、名前はMyImage.cshtml で追加。

MyImage.cshtmlを展開して、MyImage.cshtml.cs に下記の通りプログラムします。

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using SkiaSharp;

namespace SkiaSharpOnLinux.Pages
{
    public class MyImageModel : PageModel
    {
        public IActionResult OnGet()
        {
            using SKBitmap bitmap = DrawImage();
            byte[] png = ToPng(bitmap);

            return this.File(png, "image/png");
        }

        private SKBitmap DrawImage()
        {
            SKBitmap bitmap = new SKBitmap(200, 200);

            using SKPaint paint = new SKPaint();

            paint.Shader = SKShader.CreateLinearGradient(
                new SKPoint(0, 0),
                new SKPoint(200, 200),
                new SKColor[] { SKColors.Red, SKColors.Blue },
                new float[] { 0, 1 },
                SKShaderTileMode.Repeat);

            using SKCanvas canvas = new SKCanvas(bitmap);

            SKPath path = new SKPath();
            path.MoveTo(100, 0);
            path.LineTo(200, 173);
            path.LineTo(0, 173);
            path.Close();

            canvas.DrawPath(path, paint);

            return bitmap;
        }

        private byte[] ToPng(SKBitmap bitmap)
        {
            var data = SKImage.FromBitmap(bitmap).Encode(SKEncodedImageFormat.Png, 100);
            return data.ToArray();
        }
    }
}

 

3-5.Indexページからの呼び出し

ソリューションエクスプローラーでPages/Index.cshtmlをダブルクリック。

内容を下記に置き換えます。

@page
@model IndexModel
@{
    ViewData["Title"] = "C# Graphics Test";
    string os = Environment.OSVersion.ToString();
}

<div class="text-center">
    <h1 class="display-4">Welcome</h1>
    <h2>@os</h2>
    <image src="/MyImage" />
</div>

 

3-6.完成

Visual Studioで実行ボタンの右にある小さな三角(▼)をクリックして「WSL」を選ぶとWindows上のLinux環境WSLでこのWebアプリを試せます。WSLをインストールしていない場合はインストールする必要があります。(その方法はネットで調べてください。)

ブラウザーが起動して、グラデーションの三角形が表示されます。

 

おまけ1:クラウドで動かす

WSLで動作しても、クラウド上のLinuxでは動作しない場合があります。WSLに存在しているパッケージが他のLinuxに存在していないかもしれないからです。

ここでは GCP の Cloud Run で動作させてみます。

Cloud RunはWebアプリを簡単に稼働させられるクラウドの環境で料金も安いです。

使用するにはあらかじめGCPのプロジェクトを所有している必要があります。

また、ここではデプロイするのにGoogle Cloud SDKを使いますので、それもインストールして初期設定(gcloud init)も終えておく必要があります。

Cloud Runでは Cloud Run側で定義されている環境変数 PORT で指定されたポートでリッスンする必要があるので、Program.csを修正します。一番下にある app.Run() を次の3行に置き換えます、

var port = Environment.GetEnvironmentVariable("PORT") ?? "8080";
var url = $"http://0.0.0.0:{port}";
app.Run(url);

このプログラムは PORT 環境変数が存在しない場合は 8080ポートでリッスンするので、Visual Studioの実行ボタンを押しても自動で実行できなくなります。Visual Studioでデバッグのプロパティを変更するか、Visual Studioで実行後、自分でブラウザーを開いて http://localhost:8080/ にアクセスすることで動作確認できます。

次にPowerShellを開いて SkiaSharpOnLinux.csproj があるフォルダーをカレントフォルダーにします。

ここからデプロイを開始しますが、複数のGCPのプロジェクトを利用している方は gcloud config list コマンドを実行してデフォルトのプロジェクトを確認しておいてください。そのプロジェクトにこのプログラムがデプロイされます。

下記コマンドを実行します。

gcloud run deploy

Source code locationは何も入力せず Enter を押します。

Service name も何も入力せず Enter を押します。

Please enter your numeric choice では 3 を入力して Enter を押します。

3 は東京のデータセンターを利用することを意味しています。他のデータセンターでも構いません。

Allow unauthenticated invocation では y を入力して Enter を押します。

認証のないアクセスを許可するという意味です。これでこのWebアプリはURLまたはあなたが利用するCloud RunのIPアドレスさえわかれば世界中の誰でもアクセスできることになります。

しばらく待つと Cloud Runへのデプロイが完了し、次のようなURLが表示されます。

Service URL: https://xxxxxxxxx.a.run.app

これがあなたのアプリケーションの生の(オリジンの)URLです。ブラウザーでアクセスしてみてください。

なお、会社の業務で使用する場合など本格的にWebアプリを公開する場合は、このURLをそのまま使用しないでロードバランサーやWAFやCDN(Cloud Armorなど)を経由するようにして、アプリケーションのセキュリティを向上させたり、認証に対応させたりするようにしてくださいね。インターネットの世界はすぐに攻撃が飛んでくるんで怖いんです。

 

おまけ2:System.TypeInitializationException

以上で説明した通りにやればLinux上でSkiaSharpが動作するはずです。

コンテナ(mcr.microsoft.com/dotnet/aspnet:6.0)内でも動作することを私は確認しています。

ポイントは SkiaSharp.NativeAssets.Linux.NoDependencies (バージョン:2.80.3) をインストールすることです。

少しやり方が違うと SkiaSharp を使ったプログラムを Linux 上で動かすと次のエラーになる場合があります。

System.TypeInitializationException

Unable to load shared library 'libSkiaSharp' or one of its dependencies. In order to help diagnose loading problems, consider setting the LD_DEBUG environment variable: liblibSkiaSharp: cannot open shared object file: No such file or directory

この問題についてここで詳しくやりとりされています。

https://github.com/mono/SkiaSharp/issues/1341

この問題について、Linux上に libfontconfig がないのが直接の原因のようなので apt-get でこれをインストールすることでも解決できます。ただし、正攻法ではなくあとでいろいろ苦しくなると思うので参考情報程度の扱ってください。

dockerfile内に記述する場合はこんな感じです。

RUN apt-get update && apt-get install -y libfontconfig