C#:在 SKCanvas 中绘图时出现 AccessViolationException
C#: AccessViolationException when drawing in SKCanvas
简介
我正在创建我的第一个 Xamarin 应用程序(它将首先针对 UWP,然后 Android,最后可能 iOS)。
基本上,应用程序应该检测到多个手指,圆圈会在每个手指上弹出并跟随它们。
问题
我在 MR.gestures.
的 onPanning
方法上尝试绘制静态 SKCanvas
时出现 System.AccessViolationException
截图
详情
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>
结果
有什么想法吗?
您将收到该错误,因为您正在保存 canvas。 canvas 在每个帧渲染时重新创建,因此您无法控制它。
正确的方法是只在绘画方法中引用 canvas。然后,在您的填充方法中,只需使用 InvalidateSurface
.
使表面无效
这将触发重绘,然后您可以在新位置绘制项目。
class MainPage {
List<Point> touches;
void onPan() {
touches = GetTouchPoints();
view.InvalidateSurface();
}
void onPaint() {
var canvas = ...;
canvas.Draw(...);
}
}
简介
我正在创建我的第一个 Xamarin 应用程序(它将首先针对 UWP,然后 Android,最后可能 iOS)。
基本上,应用程序应该检测到多个手指,圆圈会在每个手指上弹出并跟随它们。
问题
我在 MR.gestures.
的onPanning
方法上尝试绘制静态 SKCanvas
时出现 System.AccessViolationException
截图
详情
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>
结果
有什么想法吗?
您将收到该错误,因为您正在保存 canvas。 canvas 在每个帧渲染时重新创建,因此您无法控制它。
正确的方法是只在绘画方法中引用 canvas。然后,在您的填充方法中,只需使用 InvalidateSurface
.
这将触发重绘,然后您可以在新位置绘制项目。
class MainPage {
List<Point> touches;
void onPan() {
touches = GetTouchPoints();
view.InvalidateSurface();
}
void onPaint() {
var canvas = ...;
canvas.Draw(...);
}
}