LoginSignup
6
6

More than 5 years have passed since last update.

【Xamarin】SkiaSharpを使ってお絵かきツールを作る

Posted at

やりたいこと

画面をタッチした奇跡を線で結び、お絵かきツールのようなものを作る。

前回はSkiaSharpで描画した画像をViewに表示するところをやりましたが、それだけだと、お絵かきツールのようにリアルタイムに描画を更新するような場合、画像がカクカクしてしまいます。
滑らかに描画するために、試行錯誤していたのですが、結局SkiaSharpが用意しているSKCanvasViewを使って、描画したキャンバスを直接表示することにしました。

描画ライブラリ

まずは、描画のためのキャンバスを用意します。
前回作ったCanvasCreateメソッドを元にしていますが、今回は何も返さず、キャンバスを作るだけにとどめました。

SkiaSharpTest.cs
        public void CreateCanvas(int width, int height)
        {

            surface = SKSurface.Create(width: width, height: height, colorType: SKImageInfo.PlatformColorType, alphaType: SKAlphaType.Premul);
            canvas = surface.Canvas;

            canvas.Clear(SKColors.White);

            // set up drawing tools
            paint = new SKPaint();
            paint.IsAntialias = true;
            paint.Color = new SKColor(0x2c, 0x3e, 0x50);
            paint.StrokeCap = SKStrokeCap.Round;
            paint.StrokeWidth = 10f;
        }

キャンバスを表示するビュー

iOSの場合

1.SkiaSharp Views & Layersパッケージを入れる。
iOSプロジェクトを右クリック→追加(A)→NuGet パッケージの追加(P)
  SkiaSharp Views & Layersを検索して追加する。
を検索して追加する
スクリーンショット 2016-12-05 17.37.51.png

2.描画させるビューをSKCanvasViewにする

CanvasVie.cs
using CoreGraphics;
using SkiaSharp;
using SkiaSharp.Views.iOS;
...
public partial class CanvasView : SKCanvasView

CGPointを使うのでCoreGraphicsをusingしています。SKPointにすればこれはいらないことに後で気づきました。。。

Androidの場合

1.SkiaSharp Views & Layersパッケージを入れる。
Androidプロジェクトを右クリック→追加(A)→NuGet パッケージの追加(P)
  SkiaSharp Views & Layersを検索して追加する。
を検索して追加する

2.描画させるビューをSKCanvasViewにする。

CanvasVie.cs
using Android.Graphics;
using SkiaSharp;
using SkiaSharp.Views.Android;
...
public class CanvasView : SKCanvasView

こちらも、Pointを使うのでGraphicsをusingしています。

タッチポイントを取得する

iOSの場合

方法は色々ありますが、今回は簡単にTouchesbegan(開始),TouchesMoved(移動),TouchesEnded(終了)を使いました。

CanvasView.cs
        // タッチイベント
        public override void TouchesBegan(NSSet touches, UIEvent evt)
        {
            base.TouchesBegan(touches, evt);

            UITouch touch = touches.AnyObject as UITouch;
            if (touch != null)
            {
                tPoint = touch.LocationInView(this); // タッチしたポイントを取得
            }
        }

        public override void TouchesMoved(NSSet touches, UIEvent evt)
        {
            base.TouchesMoved(touches, evt);

            UITouch touch = touches.AnyObject as UITouch;
            if (touch != null)
            {
                CGPoint nowPoint = touch.LocationInView(this); // タッチしたポイントを取得
                drawImage(nowPoint); // 描画メソッド

                tPoint = nowPoint; // 次の開始点
            }
        }

        public override void TouchesEnded(NSSet touches, UIEvent evt)
        {
            base.TouchesEnded(touches, evt);

            UITouch touch = touches.AnyObject as UITouch;
            if (touch != null)
            {
                CGPoint nowPoint = touch.LocationInView(this); // タッチしたポイントを取得
                drawImage(nowPoint); // 描画メソッド
            }
        }

        // 描画メソッド
        public void drawImage(CGPoint nowPoint)
        {
            pCanvas.Paint((float)tPoint.X, (float)tPoint.Y, (float)nowPoint.X, (float)nowPoint.Y); // 描画ライブラリで線を引く

            this.SetNeedsDisplay(); // 再描画
        }

Androidの場合

Viewでタッチイベントを取得するにはOnTouchEventを使います。

CanvasView.cs
        // タッチイベント
        public override bool OnTouchEvent(MotionEvent e)
        {
            int posX = (int)e.GetX();   // タッチポイントX
            int posY = (int)e.GetY();   // タッチポイントY

            Point newPoint = new Point(posX, posY);// タッチしたポイントを取得

            switch (e.Action)
            {
                case MotionEventActions.Down:   // 開始
                    tPoint = newPoint; // 線の開始点
                    break; 
                case MotionEventActions.Move:   // 移動

                    drawImage(newPoint); // 描画メソッド

                    tPoint = newPoint;  // 次の開始点
                    break;
                case MotionEventActions.Up:     // 終了

                    drawImage(newPoint); // 描画メソッド
                    break;
                default:
                    break;
            }

            return true;
        }

        // 描画メソッド
        public void drawImage(Point nowPoint)
        {
            pCanvas.Paint((float)tPoint.X, (float)tPoint.Y, (float)nowPoint.X, (float)nowPoint.Y); // 描画ライブラリで線を引く

            Invalidate(); // 再描画
        }

描画ライブラリ

CanvasCreateで設定したpaintで線を引きます。

SkiaSharpTest.cs
        // 線を描画
        public void Paint(float startX, float startY, float endX, float endY)
        {
            if (canvas == null)
            {
                System.Diagnostics.Debug.WriteLine("canvas is NULL");
                return;
            }

            canvas.DrawLine(startX, startY, endX, endY, paint);
        }

線を引いた画像を表示する

再描画のたびに、Drawメソッドで画面に画像を表示させます。
線を引くメソッドと、キャンバスのキャプチャを取得するメソッドを用意。今回はネイティブ側もSkiaSharpを入れているので、SKImageを渡します。
毎回Streamに変換する処理時間が勿体無いので。

iOSの場合

SKCanvasViewのDrawInSurfaceを使うと、キャンバスに直接SKImageを描画できます。

CanvasView.cs
        public override void DrawInSurface(SKSurface surface, SKImageInfo info)
        {
            base.DrawInSurface(surface, info);

            var localcanvas = surface.Canvas;
            using (var img = pCanvas.getImageImage())
            {
                localcanvas.DrawImage(img, localcanvas.ClipBounds, null);
            }

Androidの場合

SKCanvasViewが用意しているOnDraw(SKSurface surface, SKImageInfo info)を使います。
引数が通常のOnDrawと違うのでご注意ください。

CanvasView.cs
        protected override void OnDraw(SKSurface surface, SKImageInfo info)
        {
            base.OnDraw(surface, info);

            var canvas = surface.Canvas;
            using (var img = pCanvas.getImageImage())
            {
                canvas.DrawImage(img, canvas.ClipBounds, null);
            }
        }

描画ライブラリ

描画キャンバスのキャプチャ画像を返すメソッドを作ります。

SkiaSharpTest.cs

        // 画像を取得
        public SKImage getImageImage()
        {
            if (surface == null)
            {
                System.Diagnostics.Debug.WriteLine("surface is NULL");
                return null;
            }

            SKImage img = surface.Snapshot(); // 画像取得

            return img;
        }

これで、お絵かきツールの元ができました。
あとは、線を変えたり、キャンバスサイズを変えたりと自由自在に使いましょう〜。

滑らかに動かすために、変更箇所だけを再描画したりするのですが、今回はそれをしなくてもかなり滑らかに描画できているので、そのままにしています。
ちょっとカクカクするなーって気になった方は、線を引いたところだけを再描画するように工夫すると良いかも。

6
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
6