Forum Xamarin.Forms

Announcement:

The Xamarin Forums have officially moved to the new Microsoft Q&A experience. Microsoft Q&A is the home for technical questions and answers at across all products at Microsoft now including Xamarin!

To create new threads and ask questions head over to Microsoft Q&A for .NET and get involved today.

SkiaSharp SKCanvas/SKPaint AccessViolation Error

DarthSerpicalDarthSerpical USMember ✭✭
edited November 2020 in Xamarin.Forms

I am accessing the SKCanvas of an SKSurface in my PaintSurface function. This function fires I get the error: System.AccessViolationException: 'Attempted to read or write protected memory. This is often an indication that other memory is corrupt.'

private async void cnv_PaintSurface(object sender, SkiaSharp.Views.UWP.SKPaintSurfaceEventArgs e)
    {
        SKSurface surface = e.Surface;
        SKCanvas canvas = surface.Canvas;

        svg.Load(await GetStream());

        using (SKPaint paint = new SKPaint())
        {
            canvas.Clear(SKColors.Black);  //error occurs here
            canvas.DrawPicture(svg.Picture, paint);
        }
    }

    private async Task<Stream> GetStream()
    {
        StorageFile file = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFileAsync("myimage.svg");

        var inputStream = await file.OpenReadAsync();
        return inputStream.AsStreamForRead();
    }       

Best Answer

Answers

  • LeonLuLeonLu Member, Xamarin Team Xamurai
    edited November 2020

    Please add your SVG file to the PCL folder. And set the build action to Embedded resource like following screenshot

    Install following nuget packages.

    Create icon.cs with following code.

    using System;
    using System.IO;
    using SkiaSharp;
    using SkiaSharp.Views.Forms;
    using Xamarin.Forms;
    using SKSvg = SkiaSharp.Extended.Svg.SKSvg;
    
    namespace SvgXF
    {
        public class Icon : Frame
        {
            #region Private Members
    
            private readonly SKCanvasView _canvasView = new SKCanvasView();
    
            #endregion
    
            #region Bindable Properties
    
            #region ResourceId
    
            public static readonly BindableProperty ResourceIdProperty = BindableProperty.Create(
                nameof(ResourceId), typeof(string), typeof(Icon), default(string), propertyChanged: RedrawCanvas);
    
            public string ResourceId
            {
                get => (string)GetValue(ResourceIdProperty);
                set => SetValue(ResourceIdProperty, value);
            }
    
            #endregion
    
            #endregion
    
            #region Constructor
    
            public Icon()
            {
                Padding = new Thickness(0);
                BackgroundColor = Color.Transparent;
                HasShadow = false;
                Content = _canvasView;
                _canvasView.PaintSurface += CanvasViewOnPaintSurface;
            }
    
            #endregion
    
            #region Private Methods
    
            private static void RedrawCanvas(BindableObject bindable, object oldvalue, object newvalue)
            {
                Icon svgIcon = bindable as Icon;
                svgIcon?._canvasView.InvalidateSurface();
            }
    
            private void CanvasViewOnPaintSurface(object sender, SKPaintSurfaceEventArgs args)
            {
                SKCanvas canvas = args.Surface.Canvas;
                canvas.Clear();
    
                if (string.IsNullOrEmpty(ResourceId))
                    return;
    
                using (Stream stream = GetType().Assembly.GetManifestResourceStream(ResourceId))
                {
                    SKSvg svg = new SKSvg();
                    svg.Load(stream);
    
                    SKImageInfo info = args.Info;
                    canvas.Translate(info.Width / 2f, info.Height / 2f);
    
                    SKRect bounds = svg.ViewBox;
                    float xRatio = info.Width / bounds.Width;
                    float yRatio = info.Height / bounds.Height;
    
                    float ratio = Math.Min(xRatio, yRatio);
    
                    canvas.Scale(ratio);
                    canvas.Translate(-bounds.MidX, -bounds.MidY);
    
                    canvas.DrawPicture(svg.Picture);
                }
            }
    
            #endregion
        }
    }
    

    Use it in the layout of xamarin forms.

     <StackLayout>
            <Label Text="Here is a cat" VerticalOptions="Center" HorizontalOptions="Center" FontSize="30"/>
            <svg:Icon ResourceId="SvgXF.Cat.svg" WidthRequest="300" HeightRequest="300" HorizontalOptions="Center" VerticalOptions="Center"/>
        </StackLayout>
    
    

    You can google: Xamarin Forms – using SVG images with SkiaSharp, here is helpful blog about it, and this blog have source code

  • DarthSerpicalDarthSerpical USMember ✭✭

    I keep getting the error. Idon;t have the svg files in my app (they'll be coming from a database as xml)\, I have just used the GetStream method to test getting the stream) Here is what I have created:

      private static async Task<Stream> GetStream()
        {
            StorageFile file = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFileAsync("car.svg");
    
            var inputStream = await file.OpenReadAsync();
            return inputStream.AsStreamForRead();
        }
    
        private async void cnv_PaintSurface(object sender, SkiaSharp.Views.UWP.SKPaintSurfaceEventArgs e)
        {
            SKCanvas canvas = e.Surface.Canvas;
            canvas.Clear();
    
    
            using (var stream = await GetStream())
            {
                SkiaSharp.Extended.Svg.SKSvg svg = new SkiaSharp.Extended.Svg.SKSvg();
                svg.Load(stream);
    
                SKImageInfo info = e.Info;
                canvas.Translate(info.Width / 2f, info.Height / 2f);
    
                SKRect bounds = svg.ViewBox;
                float xRatio = info.Width / bounds.Width;
                float yRatio = info.Height / bounds.Height;
    
                float ratio = Math.Min(xRatio, yRatio);
    
                canvas.Scale(ratio);
                canvas.Translate(-bounds.MidX, -bounds.MidY);
    
                canvas.DrawPicture(svg.Picture);
            }
    
        }
    
  • DarthSerpicalDarthSerpical USMember ✭✭

    Makes sense, thanks Leon

Sign In or Register to comment.