source/SkiaSharp.Views.Uno/SkiaSharp.Views.Uno.WinUI.Skia/SKXamlCanvas.Skia.cs (101 lines of code) (raw):
using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.WindowsRuntime;
#if WINUI
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Media.Imaging;
#else
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
#endif
#if WINDOWS || WINUI
namespace SkiaSharp.Views.Windows
#else
namespace SkiaSharp.Views.UWP
#endif
{
public partial class SKXamlCanvas : Canvas
{
private byte[] pixels;
private GCHandle pixelsHandle;
private int pixelWidth;
private int pixelHeight;
private WriteableBitmap bitmap;
public SKXamlCanvas()
{
Initialize();
}
partial void DoUnloaded() =>
FreeBitmap();
private void DoInvalidate()
{
if (designMode)
return;
if (!isVisible)
return;
if (ActualWidth <= 0 || ActualHeight <= 0)
{
CanvasSize = SKSize.Empty;
return;
}
var info = CreateBitmap(out var unscaledSize, out var dpi);
using (var surface = SKSurface.Create(info, pixelsHandle.AddrOfPinnedObject(), info.RowBytes))
{
var userVisibleSize = IgnorePixelScaling ? unscaledSize : info.Size;
CanvasSize = userVisibleSize;
if (IgnorePixelScaling)
{
var canvas = surface.Canvas;
canvas.Scale(dpi);
canvas.Save();
}
OnPaintSurface(new SKPaintSurfaceEventArgs(surface, info.WithSize(userVisibleSize), info));
}
// This implementation is not fast enough, and providing the original pixel buffer
// is needed, yet the internal `IBufferByteAccess` interface is not yet available in Uno.
// Once it is, we can replace this implementation and provide the pinned array directly
// to skia.
using (var data = bitmap.PixelBuffer.AsStream())
{
data.Write(pixels, 0, pixels.Length);
data.Flush();
}
bitmap.Invalidate();
}
private SKImageInfo CreateBitmap(out SKSizeI unscaledSize, out float dpi)
{
var size = CreateSize(out unscaledSize, out dpi);
var info = new SKImageInfo(size.Width, size.Height, SKColorType.Bgra8888, SKAlphaType.Premul);
if (bitmap?.PixelWidth != info.Width || bitmap?.PixelHeight != info.Height)
FreeBitmap();
if (bitmap == null && info.Width > 0 && info.Height > 0)
{
bitmap = new WriteableBitmap(info.Width, info.Height);
var brush = new ImageBrush
{
ImageSource = bitmap,
AlignmentX = AlignmentX.Left,
AlignmentY = AlignmentY.Top,
Stretch = Stretch.Fill
};
Background = brush;
}
if (pixels == null || pixelWidth != info.Width || pixelHeight != info.Height)
{
FreeBitmap();
pixels = new byte[info.BytesSize];
pixelsHandle = GCHandle.Alloc(pixels, GCHandleType.Pinned);
pixelWidth = info.Width;
pixelHeight = info.Height;
}
return info;
}
private void FreeBitmap()
{
if (pixels != null)
{
pixelsHandle.Free();
pixels = null;
bitmap = null;
}
}
}
}