The most efficient way to draw in SkiaSharp without using PaintSurface event
Asked Answered
S

1

6

An extension of this question. I'm using SkiaSharp to draw shapes with OpenGL. Most of code samples for WPF suggest using SKElement control and get reference to the drawing surface inside PaintSurface event. I need reference to drawing surface al the time, not only inside of this event, so I'm using SKBitmap and SKCanvas and send the output to Image control in WPF.

XAML

<Canvas x:Name="ScreenControl" />

CS

var bmp = new SKBitmap((int)ScreenControl.ActualWidth, (int)ScreenControl.ActualHeight);
var canvas = new SKCanvas(bmp);
var image = new Image
{
  Stretch = Stretch.Fill
};

canvas.DrawText("Demo", new SKPoint(20, 20), new SKPaint { TextSize = 20 });

var index = 0;
var clock = new Timer();
var wrt = bmp.ToWriteableBitmap();

image.Source = wrt;

clock.Interval = 100;
clock.Elapsed += (object sender, ElapsedEventArgs e) =>
{
  Application.Current.Dispatcher.BeginInvoke(new Action(() =>
  {
    var wrt = bmp.ToWriteableBitmap(); // Potentially slow line #1

    canvas.Clear();
    canvas.DrawText((index++).ToString(), new SKPoint(20, 20), new SKPaint { TextSize = 20 });
    image.Source = wrt;                // Potentially slow line #2
    image.InvalidateVisual();
  }));
};
clock.Start();

ScreenControl.Children.Clear();
ScreenControl.Children.Add(image);

Project

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net5.0-windows</TargetFramework>
    <UseWPF>true</UseWPF>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="SkiaSharp.Views.WPF" Version="2.80.3-preview.18" />
  </ItemGroup>

</Project>

Question

Is there a way to draw faster, specifically, without recreating bitmap bmp.ToWriteableBitmap() and without changing image source every time image.Source = wrt? Back buffer or something related?

Suchta answered 29/12, 2020 at 16:28 Comment(1)
Don't convert to a WPF Image. Simply render your offscreen bitmap on to an SKElement, in that element's PaintSurface event. Something like protected override void OnPaintSurface(SKPaintSurfaceEventArgs e) { e.Surface.Canvas.DrawBitmap(bmp, 0, 0); }Waybill
R
0

You have to use Skia's SKElement:

<Grid>
    <skia:SKElement PaintSurface="OnPaintSurface" IgnorePixelScaling="True" />
</Grid>

And draw in OnPaintSurface:

    private void OnPaintSurface(object sender, SKPaintSurfaceEventArgs e)
    {
        // the the canvas and properties
        var canvas = e.Surface.Canvas;

        // make sure the canvas is blank
        canvas.Clear(SKColors.White);

        // draw some text
        var paint = new SKPaint
        {
            Color = SKColors.Black,
            IsAntialias = true,
            Style = SKPaintStyle.Fill,
            TextAlign = SKTextAlign.Center,
            TextSize = 24
        };
        var coord = new SKPoint(e.Info.Width / 2, (e.Info.Height + paint.TextSize) / 2);
        canvas.DrawText("SkiaSharp", coord, paint);
    }
Redevelop answered 13/7, 2022 at 12:3 Comment(1)
Does not answer the question. The entire point of the question is to NOT make the SKIA calls (such as DrawText) on that SKElement. Rather, to make those calls into an offscreen SKBitmap.Waybill

© 2022 - 2025 — McMap. All rights reserved.