-
Notifications
You must be signed in to change notification settings - Fork 539
/
SKXamlCanvas.Skia.cs
128 lines (105 loc) · 2.88 KB
/
SKXamlCanvas.Skia.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
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, SKImageInfo.PlatformColorType, 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;
}
}
}
}