GraphicsLibrary SkiaSharp Sample Demo

<font color="blue" ><B>
SkiaSharpはGoogle社のオープンソース 2D CG専用ライブラリです。
</B></FONT>

対応プラットフォームは、
- .NET Core / .NET Standard 1.3 /Linux
Xamarin.Android　Xamarin.iOS　Xamarin.tvOS　Xamarin.Mac
Windows Classic Desktop (Windows.Forms / WPF)
Windows UWP (Desktop / Mobile / Xbox / HoloLens) 等多岐にわたります。
- ザマリンが推奨しているライブラリでC++で作られています。
- 詳細は公式サイト -> <a href="https://api.skia.org/annotated.html">開発元公式H.P.</A>

::: note info
Nuget でSkiaSharpとSkiaSharp.View.WPFを入れます。
※ここに掲載したXAMLは共通です。
:::

- InvalidateVisual()を呼ぶとPaintSurface()が呼ばれ、その中で描画します。
- PaintSurface()内でTask.Delay()等は使用できません。
- 日本語解説サイトが皆無。有ったとしても殆どがザマリン用。
- アニメーション機能は特に無いが工夫次第である程度の事はできる。
 OpenGLとの組み合わせで高速化が図れるが文献が皆無で難しい。↓ 
 https://swharden.com/csdv/skiasharp/skiasharp-opengl/
 

### SkiaSharp使用サンプル/デモ (WPF使用)
https://qiita.com/inf102/items/34dab4176d50c68bf45e

https://qiita.com/inf102/items/e4c95ee481d4a232acbd

https://qiita.com/inf102/items/680c123b7791dd6eb1d1

https://qiita.com/ger531/items/6cff82b1ae14cc2c4e27

https://qiita.com/inf102/items/93cc5fd292eba3f8e02f

https://qiita.com/ger531/items/63b934264d80a5609119

https://qiita.com/ger531/items/daa65b24682d294f8b32

https://qiita.com/ger531/items/ffc70fe8385b73c945a0

https://qiita.com/ger531/items/18d7e903bc0147e33367

<font color="blue">



### 1.線
</FONT>

![OP3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821882/ffc78109-d597-ba99-7639-f5c7737bd61f.jpeg)

```c#
// SkiaSharp Test / inf102
using SkiaSharp;
using SkiaSharp.Views.Desktop;
using System;
using System.Threading.Tasks;
using System.Windows;

namespace SkiaSharpSample {

    public partial class MainWindow : Window{

        static int r=0;

	    public MainWindow()		{
		    InitializeComponent();
            this.WindowState = WindowState.Maximized;
		}

        private async  void Window_Loaded(object sender, RoutedEventArgs e) {

            while (true){
                skiaCanvas2.InvalidateVisual(); 
                
                await Task.Delay(10); 
                r+=1;
                               
            }
        }

        void PaintSurface(object sender, SKPaintSurfaceEventArgs args){

            SKImageInfo info = args.Info;
            SKSurface surface = args.Surface;
            SKCanvas canvas = surface.Canvas;
                          
            // 背景色
           canvas.Clear(SKColors.Black);
            
            SKPaint B_LINE= new SKPaint{
	            Style =  SKPaintStyle.StrokeAndFill,
			    Color = new SKColor(0, 255, 255),
                StrokeCap = (SKStrokeCap)3,
                IsAntialias=true,
                StrokeWidth = 3

            };
            canvas.DrawLine(0,0,r,1200,B_LINE);
		}
	}
}
```

- canvas.Clear(SKColors.Black)　を消すと塗りつぶされていきます。


<font color="blue">

### 2.文字

</FONT>

右から左に文字が移動して行きます。

```c#
// SkiaSharp Test

using SkiaSharp;
using SkiaSharp.Views.Desktop;
using System;
using System.Threading.Tasks;
using System.Windows;

namespace SkiaSharpSample {

    public partial class MainWindow : Window	{

        static int r=1200;

		public MainWindow()		{
			InitializeComponent();
            this.WindowState = WindowState.Maximized;
		}

        private async  void Window_Loaded(object sender, RoutedEventArgs e) {

            while (true){
                skiaCanvas2.InvalidateVisual(); 
                
                await Task.Delay(10); 
                r--;
                               
            }
        }

		void PaintSurface(object sender, SKPaintSurfaceEventArgs args){

            SKImageInfo info = args.Info;
            SKSurface surface = args.Surface;
            SKCanvas canvas = surface.Canvas;
               
            // 背景色
            canvas.Clear(SKColors.Black);
            
            var skPaint = new SKPaint() {
                TextSize=45,
                TextAlign= (SKTextAlign)SKTextAlign.Center
               // Typeface = SKTypeface.FromFamilyName("LINE Seed JP_OTF")

            };
            skPaint.Color=SKColors.Red;
            canvas.DrawText("SkiaSharp",r,200,skPaint);
		}
	}
}
```
<HR>
<font color="blue">

### 3.日本語を扱う場合は別途、LINE Seed JP_OTF等をインストール

</FONT>

https://blog.ecbeing.tech/entry/2023/04/14/080000

https://monobook.org/wiki/SkiaSharp%E3%81%A7%E6%97%A5%E6%9C%AC%E8%AA%9E%E6%96%87%E5%AD%97%E5%88%97%E3%82%92%E6%8F%8F%E7%94%BB%E3%81%99%E3%82%8B

<HR>

<font color="blue">

### 4.応用 円で渦巻模様を描く

</FONT>



<iframe width="560" height="315" src="https://www.youtube.com/embed/ucDTANkAVu0?si=oc1Cb-DD2tr3G693" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

```c#
// SkiaSharp Test (c)inf102 2023.

using SkiaSharp;
using SkiaSharp.Views.Desktop;
using System;
using System.Threading.Tasks;
using System.Windows;

namespace SkiaSharpSample {

    public partial class MainWindow : Window	{

        static int R=0;

		public MainWindow()		{
			InitializeComponent();
            this.WindowState = WindowState.Maximized;
		}

        private async  void Window_Loaded(object sender, RoutedEventArgs e) {

            while (true){
                skiaCanvas2.InvalidateVisual(); 
                
                await Task.Delay(5); 
                R+=1;
                               
            }
        }

		void PaintSurface(object sender, SKPaintSurfaceEventArgs args){

            SKImageInfo info = args.Info;
            SKSurface surface = args.Surface;
            SKCanvas canvas = surface.Canvas;
           
           // 背景色
           // canvas.Clear(SKColors.Black);
            
            SKPaint B_LINE= new SKPaint{
	            Style =  SKPaintStyle.StrokeAndFill,
			    Color = new SKColor(0, 255, 0),
                StrokeCap = (SKStrokeCap)3,
                IsAntialias=true,
                StrokeWidth = 20

            };
            
            double x,y;
            x=skiaCanvas2.CanvasSize.Width  / 2 + R*Math.Sin (R*3.14/30);
            y=skiaCanvas2.CanvasSize.Height / 2 - R*Math.Cos (R*3.14/30);
            canvas.DrawCircle((float)x,(float) y ,1,B_LINE);
		}
	}
}

```
上記、canvas.Clear(SKColors.Black)を実行するよう // を外すと円が動きます。

<font color="blue">

### 5.罫線

</FONT>


・単なる線ではなく点線にすることも可能。
・タスクマネージャのパフォーマンスタブ風。

<iframe width="360" height="250" src="https://www.youtube.com/embed/MouqHvUp2BI?si=EuCMZC-n9nEvboTs" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>


```c#
// SkiaSharp Test / (c)inf102 2023.
using SkiaSharp;
using SkiaSharp.Views.Desktop;
using System;
using System.Threading.Tasks;
using System.Windows;

namespace SkiaSharpSample {

    public partial class MainWindow : Window	{

		public MainWindow()		{
			InitializeComponent();

            // 罫線パターン
            dashArray[0] = 3.5f;  //0.21 2.5 2.5
            dashArray[1] = 2.0f;
            //dashArray[2] = 2.0f;
		}
        static int XSC=500;

        // 罫線パターン
        static readonly float[] dashArray = new float[4];

        private async  void Window_Loaded(object sender, RoutedEventArgs e) {
            
            Height=247;
            Width=650;
            
            while (true){
                skiaCanvas2.InvalidateVisual(); 
                await Task.Delay(10);
                 
            }
        }


		void PaintSurface(object sender, SKPaintSurfaceEventArgs args){

            args.Surface.Canvas.Clear(SKColors.Black);

            SKPaint XY_LINE1= new SKPaint{
	            Style =  SKPaintStyle.StrokeAndFill,
                Color = new SKColor(0, 191, 200),
                IsAntialias=true,
                PathEffect = SKPathEffect.CreateDash(dashArray, 10),
                StrokeWidth =1

            };

            SKPaint XY_LINE2= new SKPaint{
	            Style =  SKPaintStyle.StrokeAndFill,
			    Color = new SKColor(00, 191, 200),
                IsAntialias=true,
                PathEffect = SKPathEffect.CreateDash(dashArray, 10),
                StrokeWidth = 1

            };

            // X
            for(int g = XSC; g > 0; g -= (int)20) {
               args.Surface.Canvas.DrawLine(g, 0, g, 200, XY_LINE1);
            }
            
            XSC-=1;

            if(XSC <= 634) XSC = 654;

            // Y
            for(int g = 201; g >= 2; g -= (int)20) {
                args.Surface.Canvas.DrawLine(0, g, 650, g, XY_LINE2);
            }
        }
	}
}
```

<font color="blue">

### 6.JPG表示

</FONT>

```c#
void SfPaintSurface(object sender, SKPaintSurfaceEventArgs args) {
  
    SKSurface surface = args.Surface;
    SKCanvas canvas = surface.Canvas;

    canvas.Clear(SKColors.Black);
    var bitmap = SKBitmap.Decode("C:\\OP1.JPG");
    canvas.DrawBitmap(bitmap,10,10);

}
```

<font color="blue">

### 7.非アフィン変換</FONT>
https://learn.microsoft.com/ja-jp/xamarin/xamarin-forms/user-interface/graphics/skiasharp/transforms/non-affine

3Dのグラフは計算が面倒なので2Dで書いた後、JPG変換と非アフィン変換を連続的にやってアニメーション化しようと思ったが現状ジャギーが目立つ。改善策は未確認。


![123.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821882/880b6bac-4a33-f9a2-776b-698265f28c25.jpeg)
↓　非アフィン変換
![ORG.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821882/099b11fe-fc88-c2a7-31ae-316b8a2b2245.jpeg)


</FONT>




```c#
void SfPaintSurface(object sender, SKPaintSurfaceEventArgs args) {
  
    var bitmap = SKBitmap.Decode("C:\\123.JPG");
    SKSurface surface = args.Surface;
    SKCanvas canvas = surface.Canvas;
    canvas.Clear(SKColors.Black);
  
    SKMatrix perspectiveMatrix = SKMatrix.MakeIdentity();
    perspectiveMatrix.Persp0 = (float)0.00152;
    perspectiveMatrix.Persp1 = (float)0.0000012;

    float xCenter = 400;
    float yCenter = 600;
           
    SKMatrix matrix = SKMatrix.MakeTranslation(-xCenter, -yCenter);
    SKMatrix.PostConcat(ref matrix, perspectiveMatrix);
    SKMatrix.PostConcat(ref matrix, SKMatrix.MakeTranslation(xCenter, yCenter));

    float x = xCenter - bitmap.Width / 2;
    float y = yCenter - bitmap.Height / 2;

    canvas.SetMatrix(matrix);
    canvas.DrawBitmap(bitmap,x,y);
}
```






<font color="blue">


### ここまでの共通XAML

</FONT>

```xml
<Window x:Class="SkiaSharpSample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:SkiaSharpSample" xmlns:skia="clr-namespace:SkiaSharp.Views.WPF;assembly=SkiaSharp.Views.WPF" 
        xmlns:wpf="clr-namespace:SkiaSharp.Views.WPF;assembly=SkiaSharp.Views.WPF" mc:Ignorable="d"
        Title="SkiaSharp" Loaded="Window_Loaded">

    <Grid  >
        <wpf:SKElement x:Name="skiaCanvas2" PaintSurface="PaintSurface"  />
    </Grid>

</Window>
```


<font color="blue">

### 8.ミサイルコマンドの作りかけ(サンプル)

</FONT>


ミサイルの発射はできたが、当たり判定、敵ミサイルの非同期、誘爆の処理が予想以上に大変で放置。
マウス左で発射。いつか完成させたい。非同期の複数ミサイルの処理が必要で発射だけでも手間がかかった。
自ミサイルの爆発コードも入ってるが不具合の為、呼び出しは行っていません。

<iframe width="560" height="315" src="https://www.youtube.com/embed/Qnp2avHs2U8?si=9N-3ex4SG_vqmfxA" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>


```c#
using Microsoft.Win32.SafeHandles;
using SkiaSharp;
using SkiaSharp.Views.Desktop;
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;

namespace SF {

    public partial class MainWindow : Window {
       
        // 広がる時間
        const int BaRate=5;

        // 広がる広さ
        const int BaArea=40;

        // マウス位置
        static int MX,MY;
        static int GX;
        static bool FIRST;

        // 標準レティクル
        static int [] Tx = new int [4];
        static int [] Ty = new int [4];

        static int BA;

        static int[,,] a = new int[1000, 200,100];

        public void msg(string ss) {
            MessageBox.Show(ss);
        }

        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        public  MainWindow() {
            InitializeComponent();

        }

        private  async void Window_Loaded(object sender, RoutedEventArgs e) {
        
            while (true){
        
                await Task.Delay(TimeSpan.FromSeconds(1.0 / 500));
        
                for (int cz=0;cz<1000;cz++){ 
                    if (a[cz,0,0] !=0) a[cz,0,4]++;
                }

                for (int cz=0;cz<1000;cz++){ 
                    if (a[cz,0,0]!=0){
                        if (a[cz,0,4] > a[cz,0,2]/20.0) { //[1]
                            a[cz,0,3]=a[cz,0,4]; // [2]
                            a[cz,0,0]=999; // LINE消去用値
                            if (a[0,0,7]!=999){
                                 _=Task.Run ( ()=>{
                                   //  await Task.Delay(TimeSpan.FromSeconds(1.0 / 500));
                                     for (int t=0;t<40;t++){
                                        Dispatcher.Invoke((Action)(()=>{
                                          //  Sf2.InvalidateVisual();
                                        }));
                                        a[0,0,7]++;
                                     }
                                 });
                                 a[0,0,7]=999;
                            }
                         break;
                        }
                    }
                }
                // 本線
                Sf.InvalidateVisual();
            }
        }   

        void SfPaintSurface2(object sender, SKPaintSurfaceEventArgs args) {
          
            SKSurface surface = args.Surface;
            SKCanvas canvas = surface.Canvas;
          
            SKPaint A_LINE = new SKPaint {
                StrokeWidth=1,
                IsAntialias = true,
             
            };

            // 拡大
            A_LINE.Color = new SKColor(255,255,255,255);
            A_LINE.Style = SKPaintStyle.Fill;

            if (a[0,0,7]==999){
            
                canvas.Clear();
                return;
            }

            canvas.DrawCircle(a[0, 0, 5], a[0, 0, 6], a[0,0,7], A_LINE);
               
        }

        // 角度
        protected int getRadian(double x, double y, double x2, double y2) {
            double radian = Math.Atan2(y2 - y,x2 - x);
            return (int)(radian*180d/3.14);
        }

        protected int getDistance(double x, double y, double x2, double y2) {
            double distance = Math.Sqrt((x2 - x) * (x2 - x) + (y2 - y) * (y2 - y));
            return (int) distance;
        }

        private void Window_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e) {
       
            System.Windows.Point point = e.GetPosition(this);     
            MX=(int)point.X;
            MY=(int)point.Y;

            // 標準レティクル
            Tx[0]=(int)(MX + (10)* Math.Sin ((-45-270) * Math.PI/180)); Ty[0]=(int)(MY - (10)* Math.Cos ((-45-270) * Math.PI/180));
            Tx[1]=(int)(MX + (10)* Math.Sin ((45-270) * Math.PI/180));  Ty[1]=(int)(MY - (10)* Math.Cos ((45-270) * Math.PI/180));
            Tx[2]=(int)(MX + (10)* Math.Sin ((135-270) * Math.PI/180)); Ty[2]=(int)(MY - (10)* Math.Cos ((135-270) * Math.PI/180));
            Tx[3]=(int)(MX + (10)* Math.Sin ((225-270) * Math.PI/180)); Ty[3]=(int)(MY - (10)* Math.Cos ((225-270) * Math.PI/180));

            // 最大長
            int LL=getDistance (MX,MY,959,1147);

            // 角度
            int RA=getRadian (959,1147,MX,MY);
        
            // 本数毎に長さ保存 
            a[GX,0,2]=LL;

            // 標準レティクル表示用マウス位置
            a[GX,0,5]=MX;
            a[GX,0,6]=MY;

            int vz=0;
            for (int w=0;w<100;w++){
                a[GX,w,0]=(int)(959 +  (vz)* Math.Sin ((RA-270.7) * Math.PI/180));
                a[GX,w,1]=(int)(1147 - (vz)* Math.Cos ((RA-270.7) * Math.PI/180));
                vz+=20;
            }
            GX++; // 本数
        }

        // 本線
        void SfPaintSurface(object sender, SKPaintSurfaceEventArgs args) {

            SKSurface surface = args.Surface;
            SKCanvas canvas = surface.Canvas;
         
            if (FIRST==false){
                canvas.Clear();
                canvas.Clear(SKColors.Black);
                FIRST=true;
            }

            SKPaint A_LINE= new SKPaint{
	            IsAntialias=true,
               
            };
          
            // 標準レティクル
            A_LINE.StrokeWidth = 2;
            A_LINE.Color = new SKColor(255,255,255,100);

            for (int cz=0;cz<1000;cz++){ 
                if (a[cz,0,0]!=0) for (int tx=0;tx<4;tx++) canvas.DrawLine(MX,MY,Tx[tx],Ty[tx],A_LINE);
            }

            // LUNCH CLEAR
            for (int cz=0;cz<1000;cz++){ 
                if (a[cz,0,0]==999){
                    a[cz,0,0]=0;
                    A_LINE.StrokeWidth = 5;
                    A_LINE.Color = new SKColor(0,0,0,255);
                    int ma=a[cz,0,3];
                    canvas.DrawLine(959,1147,(float)a[cz,ma,0],(float)a[cz,ma,1],A_LINE);
                }
            }
            //////////////////////////////////////////////////////////////////////////
            // 発射
            //////////////////////////////////////////////////////////////////////////
            A_LINE.Color = new SKColor(0,255,0,255);
            int CrentLL;

            for (int cz=0;cz<1000;cz++){ 
                if(a[cz, 0, 0] != 0 ) {
                    if (a[cz,0,0] ==999) continue;
                    A_LINE.StrokeWidth = 2;
                    CrentLL=a[cz,0,4];
                    canvas.DrawLine(959, 1147, (float)a[cz, CrentLL, 0], (float)a[cz, CrentLL, 1], A_LINE);
                }
            }
        }
    }
}
```

```xml
<Window x:Class="SF.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:wpf="clr-namespace:SkiaSharp.Views.WPF;assembly=SkiaSharp.Views.WPF"
        xmlns:local="clr-namespace:SF" mc:Ignorable="d" 
        Title="StatField" Height="1200" Width="1920" Loaded="Window_Loaded" 
	WindowState="Maximized" PreviewMouseLeftButtonDown="Window_PreviewMouseLeftButtonDown"  >
  
    <Grid>
        <wpf:SKElement x:Name="Sf"    PaintSurface="SfPaintSurface" />
        <wpf:SKElement x:Name="Sf2"    PaintSurface="SfPaintSurface2" />
    </Grid>
</Window>
```

<font color="blue">

### 9.PDF出力
頑張ればWYSIWYGエディッタできます。
完成させたんですがC#のverが変わったせいか動作しなくなりました。

</FONT>


``` c#

using SkiaSharp;
using SkiaSharp.Views.Desktop;
using System;
using System.Threading.Tasks;
using System.Windows;

namespace SkiaSharpSample {

    public partial class MainWindow : Window	{
        SKPaint PaintSkia = new SKPaint{
	        Style = SKPaintStyle.Fill,
            IsAntialias=true,    
            TextSize=30,
            Color=new SKColor (255,0,0),
            Typeface = SKTypeface.FromFamilyName("LINE Seed JP_OTF")
            
        };

		public MainWindow()		{
			InitializeComponent();
       
        }
         
        private void Window_Loaded(object sender, RoutedEventArgs e) {

            var stream = SKFileWStream.OpenStream(@"c:\SkiaPDFFile.pdf");
            var document = SKDocument.CreatePdf(stream);
            var canvas = document.BeginPage(794,1123);  //794,.1123
            
            canvas.DrawText("申込書",100,100,PaintSkia);
            canvas.DrawLine(0,0,100,200,PaintSkia);

            document.EndPage();
            document.Close();

            MainCanvas.InvalidateVisual();

        }

        void MainPaintSurface(object sender, SKPaintSurfaceEventArgs args){
            args.Surface.Canvas.Clear(SKColors.Black);
            args.Surface.Canvas.DrawText("xxxx申込書",100,100,PaintSkia);
            args.Surface.Canvas.DrawLine(0,0,200,200,PaintSkia);
        }
    }
}
```
PDF用 XAML
``` xml
<Window x:Class="SkiaSharpSample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:SkiaSharpSample" xmlns:skia="clr-namespace:SkiaSharp.Views.WPF;assembly=SkiaSharp.Views.WPF" 
        xmlns:wpf="clr-namespace:SkiaSharp.Views.WPF;assembly=SkiaSharp.Views.WPF" mc:Ignorable="d"
        Title="WPF+SkiaSharp PDF" Loaded="Window_Loaded" Height="900" Width="1300"   >

    <Grid  >
        <wpf:SKElement x:Name="MainCanvas" PaintSurface="MainPaintSurface" Grid.Column="0" Grid.Row="0"/>
    </Grid>

</Window>
```