I am creating my first Xamarin app (that will target UWP first, then Android, finally maybe iOS).
Basically, the app should detect multiple fingers and circles will pop over each finger and follow them.
I am having an System.AccessViolationException
when trying to draw on a staic SKCanvas
on my onPanning
method from MR.gestures.
L'exception System.AccessViolationException s'est produite
HResult=0x80004003 Message=Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Source= Arborescence des appels de procédure : à SkiaSharp.SkiaApi.sk_canvas_draw_circle(IntPtr t, Single cx, Single cy, Single radius, IntPtr paint) à SkiaSharp.SKCanvas.DrawCircle(Single cx, Single cy, Single radius, SKPaint paint) à App1.MainPage.drawCircleOnCanvas(Point pointerPos, Int32 radius) dans d:\Profiles\qpollet\Documents\Visual Studio 2017\Projects\App1\App1\App1\MainPage.xaml.cs :ligne 70 à App1.MainPage.onPanning(Object sender, PanEventArgs e) dans d:\Profiles\qpollet\Documents\Visual Studio 2017\Projects\App1\App1\App1\MainPage.xaml.cs :ligne 54 à MR.Gestures.GestureHandler.<>c__DisplayClass115_0`1.b__0() à Xamarin.Forms.Platform.UWP.WindowsBasePlatformServices.<>c__DisplayClass2_0.b__0()
MainPage.xaml.cs
using SkiaSharp;
using SkiaSharp.Views.Forms;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using MR.Gestures;
namespace App1
{
public partial class MainPage : Xamarin.Forms.ContentPage
{
private static SKCanvas canvas;
public MainPage()
{
InitializeComponent();
}
private void OnPainting(object sender, SKPaintSurfaceEventArgs e)
{
canvas = e.Surface.Canvas;
Point pointerPos = new Point
{
X = 200,
Y = 400
};
drawCircleOnCanvas(pointerPos, 50);
Point pointerPos1 = new Point
{
X = 1000,
Y = 600
};
drawCircleOnCanvas(pointerPos1, 50);
}
private void onPanning(object sender, PanEventArgs e)
{
Debug.WriteLine("MR Panning !!!!!");
try
{
List<Point> touches = e.Touches.ToList();
//foreach (Point touch in touches)
for(int i = 0; i <= touches.Count; i++)
{
Point touch = touches[i];
Debug.WriteLine("index " + i + " : " + touch.X + " " + touch.Y);
drawCircleOnCanvas(touch, 50);
}
}
catch (ArgumentNullException) { }
}
internal void drawCircleOnCanvas(Point pointerPos, int radius)
{
Debug.WriteLine(pointerPos.X + " " + pointerPos.Y);
SKPaint circleFill = new SKPaint
{
IsAntialias = true,
Style = SKPaintStyle.Fill,
Color = SKColors.Red
};
canvas.DrawCircle(((float) pointerPos.X - radius), ((float) pointerPos.Y - radius), radius, circleFill);
}
}
}
MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:App1"
xmlns:views="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
xmlns:mr="clr-namespace:MR.Gestures;assembly=MR.Gestures"
x:Class="App1.MainPage">
<Label Text="Ooooh, you touch my tralala"
VerticalOptions="Center"
HorizontalOptions="Center" />
<RelativeLayout>
<mr:AbsoluteLayout
RelativeLayout.XConstraint="{ConstraintExpression Type=Constant, Constant=0}"
RelativeLayout.YConstraint="{ConstraintExpression Type=Constant, Constant=0}"
RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width}"
RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height}"
BackgroundColor="Blue"
x:Name="touchLayout"
Panning="onPanning">
</mr:AbsoluteLayout>
<views:SKCanvasView
RelativeLayout.XConstraint="{ConstraintExpression Type=Constant, Constant=0}"
RelativeLayout.YConstraint="{ConstraintExpression Type=Constant, Constant=0}"
RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width}"
RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height}"
PaintSurface="OnPainting"/>
</RelativeLayout>
</ContentPage>
You will get that error because you are saving the canvas. The canvas is recreated on each frame render, so you cannot keep a handle to it.
The correct way is to only ever reference the canvas INSIDE the paint method. Then, in your padding method, just invalidate the surface using InvalidateSurface
.
This will trigger a redraw, and then you can draw the items in new locations.
class MainPage {
List<Point> touches;
void onPan() {
touches = GetTouchPoints();
view.InvalidateSurface();
}
void onPaint() {
var canvas = ...;
canvas.Draw(...);
}
}