やりたいこと
画面をタッチした奇跡を線で結び、お絵かきツールのようなものを作る。
前回はSkiaSharpで描画した画像をViewに表示するところをやりましたが、それだけだと、お絵かきツールのようにリアルタイムに描画を更新するような場合、画像がカクカクしてしまいます。
滑らかに描画するために、試行錯誤していたのですが、結局SkiaSharpが用意しているSKCanvasViewを使って、描画したキャンバスを直接表示することにしました。
描画ライブラリ
まずは、描画のためのキャンバスを用意します。
前回作ったCanvasCreateメソッドを元にしていますが、今回は何も返さず、キャンバスを作るだけにとどめました。
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を検索して追加する。
を検索して追加する
2.描画させるビューをSKCanvasViewにする
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にする。
using Android.Graphics;
using SkiaSharp;
using SkiaSharp.Views.Android;
...
public class CanvasView : SKCanvasView
こちらも、Pointを使うのでGraphicsをusingしています。
タッチポイントを取得する
iOSの場合
方法は色々ありますが、今回は簡単にTouchesbegan(開始),TouchesMoved(移動),TouchesEnded(終了)を使いました。
// タッチイベント
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を使います。
// タッチイベント
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で線を引きます。
// 線を描画
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を描画できます。
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と違うのでご注意ください。
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);
}
}
描画ライブラリ
描画キャンバスのキャプチャ画像を返すメソッドを作ります。
// 画像を取得
public SKImage getImageImage()
{
if (surface == null)
{
System.Diagnostics.Debug.WriteLine("surface is NULL");
return null;
}
SKImage img = surface.Snapshot(); // 画像取得
return img;
}
これで、お絵かきツールの元ができました。
あとは、線を変えたり、キャンバスサイズを変えたりと自由自在に使いましょう〜。
滑らかに動かすために、変更箇所だけを再描画したりするのですが、今回はそれをしなくてもかなり滑らかに描画できているので、そのままにしています。
ちょっとカクカクするなーって気になった方は、線を引いたところだけを再描画するように工夫すると良いかも。