Add the GPU views for Uno Platform (#1429)

* Add the GPU views for the platforms
* Add the render loop for the platforms
* Fix the samples for infinite recursion
* Added the GL views to the gallery
This commit is contained in:
Matthew Leibowitz 2020-07-27 18:46:04 +02:00 коммит произвёл GitHub
Родитель 1fa3daddcc
Коммит b3f5975cd8
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
22 изменённых файлов: 828 добавлений и 14 удалений

Просмотреть файл

@ -50,7 +50,7 @@ namespace SkiaSharpSample.Samples
private void GenerateDocument()
{
if (isSupported && File.Exists(path))
if (!isSupported || (isSupported && File.Exists(path)))
return;
var metadata = new SKDocumentPdfMetadata
@ -70,7 +70,6 @@ namespace SkiaSharpSample.Samples
if (document == null)
{
isSupported = false;
Refresh();
return;
}

Просмотреть файл

@ -52,7 +52,7 @@ namespace SkiaSharpSample.Samples
private void GenerateDocument()
{
if (isSupported && File.Exists(path))
if (!isSupported || (isSupported && File.Exists(path)))
return;
using var document = SKDocument.CreateXps(path);
@ -60,7 +60,6 @@ namespace SkiaSharpSample.Samples
if (document == null)
{
isSupported = false;
Refresh();
return;
}

Просмотреть файл

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.0" package="com.companyname.skiasharpsample" android:versionCode="1">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" />
<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:label="@string/app_name" android:theme="@style/Theme.AppCompat" android:hardwareAccelerated="true"></application>
<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:label="@string/app_name" android:theme="@style/AppTheme" android:hardwareAccelerated="true"></application>
</manifest>

Просмотреть файл

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?>
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light">
<!-- This removes the ActionBar -->
<item name="windowActionBar">false</item>
<item name="android:windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:windowNoTitle">true</item>
</style>
</resources>

Просмотреть файл

@ -44,6 +44,7 @@
<!-- the samples canvas -->
<Grid Tapped="OnSampleTapped" Background="White">
<views:SKXamlCanvas x:Name="canvas" Grid.Row="1" PaintSurface="OnPaintCanvas" />
<views:SKSwapChainPanel x:Name="glview" Grid.Row="1" PaintSurface="OnPaintGL" Visibility="Collapsed" />
</Grid>
</SplitView.Content>
</SplitView>
@ -54,6 +55,14 @@
OverflowButtonVisibility="Collapsed"
Foreground="White">
<!-- the toolbar items -->
<AppBarButton Icon="Repair" Label="Backend" IsCompact="True" Foreground="White">
<Button.Flyout>
<MenuFlyout>
<MenuFlyoutItem Text="Memory" Tag="Memory" Click="OnBackendSelected" />
<MenuFlyoutItem Text="OpenGL" Tag="OpenGL" Click="OnBackendSelected" />
</MenuFlyout>
</Button.Flyout>
</AppBarButton>
<AppBarButton
Click="OnToggleSlideshow"
Foreground="White"

Просмотреть файл

@ -58,6 +58,26 @@ namespace SkiaSharpSample
splitView.IsPaneOpen = menuButton.IsChecked == true;
}
private void OnBackendSelected(object sender, RoutedEventArgs e)
{
var menu = sender as MenuFlyoutItem;
var backend = (SampleBackends)Enum.Parse(typeof(SampleBackends), menu.Tag.ToString());
switch (backend)
{
case SampleBackends.Memory:
glview.Visibility = Visibility.Collapsed;
canvas.Visibility = Visibility.Visible;
canvas.Invalidate();
break;
case SampleBackends.OpenGL:
glview.Visibility = Visibility.Visible;
canvas.Visibility = Visibility.Collapsed;
glview.Invalidate();
break;
}
}
private void OnToggleSlideshow(object sender, RoutedEventArgs e)
{
if (cancellations != null)
@ -107,6 +127,11 @@ namespace SkiaSharpSample
OnPaintSurface(e.Surface.Canvas, e.Info.Width, e.Info.Height);
}
private void OnPaintGL(object sender, SKPaintGLSurfaceEventArgs e)
{
OnPaintSurface(e.Surface.Canvas, e.BackendRenderTarget.Width, e.BackendRenderTarget.Height);
}
private void SetSample(SampleBase newSample)
{
// clean up the old sample
@ -145,7 +170,10 @@ namespace SkiaSharpSample
private void OnRefreshRequested(object sender, EventArgs e)
{
if (canvas.Visibility == Visibility.Visible)
canvas.Invalidate();
if (glview.Visibility == Visibility.Visible)
glview.Invalidate();
}
private void OnPaintSurface(SKCanvas canvas, int width, int height)

Просмотреть файл

@ -737,6 +737,7 @@ stages:
- task: TSAUpload@1
displayName: Publish TSA logs
condition: always()
continueOnError: true
inputs:
tsaVersion: 'TsaV2'
codebase: 'NewOrUpdate'

Просмотреть файл

@ -0,0 +1,57 @@
using Android.Opengl;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace SkiaSharp.Views.UWP
{
public partial class SKSwapChainPanel : FrameworkElement
{
private SKGLTextureView glTextureView;
public SKSwapChainPanel()
{
Initialize();
}
private SKSize GetCanvasSize() =>
glTextureView?.CanvasSize ?? SKSize.Empty;
private GRContext GetGRContext() =>
glTextureView?.GRContext;
partial void DoLoaded()
{
glTextureView = new SKGLTextureView(Context);
DoEnableRenderLoop(EnableRenderLoop);
glTextureView.PaintSurface += OnPaintSurface;
AddView(glTextureView);
}
partial void DoUnloaded()
{
if (glTextureView == null)
return;
RemoveView(glTextureView);
glTextureView.PaintSurface -= OnPaintSurface;
glTextureView.Dispose();
glTextureView = null;
}
partial void DoEnableRenderLoop(bool enable)
{
if (glTextureView == null)
return;
glTextureView.RenderMode = enable
? Rendermode.Continuously
: Rendermode.WhenDirty;
}
private void DoInvalidate() =>
glTextureView?.RequestRender();
private void OnPaintSurface(object sender, SKPaintGLSurfaceEventArgs e) =>
OnPaintSurface(e);
}
}

Просмотреть файл

@ -15,6 +15,9 @@
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\SkiaSharp.Views\SkiaSharp.Views.Shared\**\*.cs" Link="%(RecursiveDir)%(Filename)%(Extension)" />
<Compile Include="..\..\SkiaSharp.Views\SkiaSharp.Views.Android\GLTextureView.cs" />
<Compile Include="..\..\SkiaSharp.Views\SkiaSharp.Views.Android\SKGLTextureView.cs" />
<Compile Include="..\..\SkiaSharp.Views\SkiaSharp.Views.Android\SKGLTextureViewRenderer.cs" />
<Compile Include="..\..\SkiaSharp.Views\SkiaSharp.Views.UWP\UWPExtensions.cs" />
<Compile Include="..\SkiaSharp.Views.Uno\**\*.cs" Link="%(RecursiveDir)%(Filename)%(Extension)" />
</ItemGroup>

Просмотреть файл

@ -0,0 +1,83 @@
using CoreVideo;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace SkiaSharp.Views.UWP
{
public partial class SKSwapChainPanel : FrameworkElement
{
private SKGLView glView;
private CVDisplayLink displayLink;
public SKSwapChainPanel()
{
Initialize();
}
private SKSize GetCanvasSize() =>
glView?.CanvasSize ?? SKSize.Empty;
private GRContext GetGRContext() =>
glView?.GRContext;
partial void DoLoaded()
{
glView = new SKGLView(Bounds);
glView.PaintSurface += OnPaintSurface;
AddSubview(glView);
}
partial void DoUnloaded()
{
DoEnableRenderLoop(false);
if (glView != null)
{
glView.RemoveFromSuperview();
glView.PaintSurface -= OnPaintSurface;
glView.Dispose();
glView = null;
}
}
private void DoInvalidate() =>
DoEnableRenderLoop(true);
private void OnPaintSurface(object sender, SKPaintGLSurfaceEventArgs e) =>
OnPaintSurface(e);
partial void DoEnableRenderLoop(bool enable)
{
// stop the render loop
if (!enable)
{
if (displayLink != null)
{
displayLink.Stop();
displayLink.Dispose();
displayLink = null;
}
return;
}
// only start if we haven't already
if (displayLink != null)
return;
// create the loop
displayLink = new CVDisplayLink();
displayLink.SetOutputCallback(delegate
{
// redraw the view
glView?.BeginInvokeOnMainThread(() => glView?.Display());
// stop the render loop if it has been disabled or the views are disposed
if (glView == null || !EnableRenderLoop)
DoEnableRenderLoop(false);
return CVReturn.Success;
});
displayLink.Start();
}
}
}

Просмотреть файл

@ -16,6 +16,7 @@
<ItemGroup>
<Compile Include="..\..\SkiaSharp.Views\SkiaSharp.Views.Shared\**\*.cs" Link="%(RecursiveDir)%(Filename)%(Extension)" />
<Compile Include="..\..\SkiaSharp.Views\SkiaSharp.Views.Apple\SKCGSurfaceFactory.cs" />
<Compile Include="..\..\SkiaSharp.Views\SkiaSharp.Views.Mac\SKGLView.cs" />
<Compile Include="..\..\SkiaSharp.Views\SkiaSharp.Views.UWP\UWPExtensions.cs" />
<Compile Include="..\SkiaSharp.Views.Uno\**\*.cs" Link="%(RecursiveDir)%(Filename)%(Extension)" />
</ItemGroup>

Просмотреть файл

@ -0,0 +1,210 @@
using System;
using System.Threading;
using Uno.Foundation;
using Uno.Foundation.Interop;
using Windows.UI.Xaml;
namespace SkiaSharp.Views.UWP
{
public partial class SKSwapChainPanel : FrameworkElement
{
private const int ResourceCacheBytes = 256 * 1024 * 1024; // 256 MB
private const SKColorType colorType = SKColorType.Rgba8888;
private const GRSurfaceOrigin surfaceOrigin = GRSurfaceOrigin.BottomLeft;
private readonly SKSwapChainPanelJsInterop jsInterop;
private GRGlInterface glInterface;
private GRContext context;
private JsInfo jsInfo;
private GRGlFramebufferInfo glInfo;
private GRBackendRenderTarget renderTarget;
private SKSurface surface;
private SKCanvas canvas;
private SKSizeI lastSize;
public SKSwapChainPanel()
: base("canvas")
{
jsInterop = new SKSwapChainPanelJsInterop(this);
Initialize();
}
private SKSize GetCanvasSize() => lastSize;
private GRContext GetGRContext() => context;
partial void DoLoaded()
{
jsInfo = jsInterop.CreateContext();
Invalidate();
}
partial void DoEnableRenderLoop(bool enable) =>
jsInterop.SetEnableRenderLoop(enable);
//partial void DoUpdateBounds() =>
// jsInterop.ResizeCanvas();
private void DoInvalidate()
{
if (designMode)
return;
if (!isVisible)
return;
if ((int)ActualWidth <= 0 || (int)ActualHeight <= 0)
return;
jsInterop.RequestAnimationFrame(EnableRenderLoop);
}
internal void RenderFrame()
{
if (!jsInfo.IsValid)
return;
// create the SkiaSharp context
if (context == null)
{
glInterface = GRGlInterface.Create();
context = GRContext.CreateGl(glInterface);
// bump the default resource cache limit
context.SetResourceCacheLimit(ResourceCacheBytes);
}
// get the new surface size
var newSize = new SKSizeI((int)(ActualWidth * ContentsScale), (int)(ActualHeight * ContentsScale));
// manage the drawing surface
if (renderTarget == null || lastSize != newSize || !renderTarget.IsValid)
{
// create or update the dimensions
lastSize = newSize;
glInfo = new GRGlFramebufferInfo(jsInfo.FboId, colorType.ToGlSizedFormat());
// destroy the old surface
surface?.Dispose();
surface = null;
canvas = null;
// re-create the render target
renderTarget?.Dispose();
renderTarget = new GRBackendRenderTarget(newSize.Width, newSize.Height, jsInfo.Samples, jsInfo.Stencil, glInfo);
}
// create the surface
if (surface == null)
{
surface = SKSurface.Create(context, renderTarget, surfaceOrigin, colorType);
canvas = surface.Canvas;
}
using (new SKAutoCanvasRestore(canvas, true))
{
// start drawing
OnPaintSurface(new SKPaintGLSurfaceEventArgs(surface, renderTarget, surfaceOrigin, colorType, glInfo));
}
// update the control
canvas.Flush();
context.Flush();
}
private struct JsInfo
{
public bool IsValid { get; set; }
public int ContextId { get; set; }
public uint FboId { get; set; }
public int Stencil { get; set; }
public int Samples { get; set; }
public int Depth { get; set; }
}
private class SKSwapChainPanelJsInterop : IJSObject, IJSObjectMetadata
{
private static long handleCounter = 0L;
private readonly long jsHandle;
public SKSwapChainPanelJsInterop(SKSwapChainPanel panel)
{
Panel = panel ?? throw new ArgumentNullException(nameof(panel));
jsHandle = Interlocked.Increment(ref handleCounter);
Handle = JSObjectHandle.Create(this, this);
}
public SKSwapChainPanel Panel { get; }
public JSObjectHandle Handle { get; }
public void RenderFrame() =>
Panel.RenderFrame();
public void RequestAnimationFrame(bool renderLoop) =>
WebAssemblyRuntime.InvokeJSWithInterop($"{this}.requestAnimationFrame({(renderLoop ? "true" : "false")});");
public void SetEnableRenderLoop(bool enable) =>
WebAssemblyRuntime.InvokeJSWithInterop($"{this}.setEnableRenderLoop({(enable ? "true" : "false")});");
public void ResizeCanvas() =>
WebAssemblyRuntime.InvokeJSWithInterop($"{this}.resizeCanvas();");
public JsInfo CreateContext()
{
var resultString = WebAssemblyRuntime.InvokeJSWithInterop($"return {this}.createContext('{Panel.HtmlId}');");
var result = resultString?.Split(',');
if (result?.Length != 5)
return default;
return new JsInfo
{
IsValid = true,
ContextId = int.Parse(result[0]),
FboId = uint.Parse(result[1]),
Stencil = int.Parse(result[2]),
Samples = int.Parse(result[3]),
Depth = int.Parse(result[4]),
};
}
long IJSObjectMetadata.CreateNativeInstance(IntPtr managedHandle)
{
WebAssemblyRuntime.InvokeJS($"SkiaSharp.Views.UWP.SKSwapChainPanel.createInstance('{managedHandle}', '{jsHandle}')");
return jsHandle;
}
string IJSObjectMetadata.GetNativeInstance(IntPtr managedHandle, long jsHandle) =>
$"SkiaSharp.Views.UWP.SKSwapChainPanel.getInstance('{jsHandle}')";
void IJSObjectMetadata.DestroyNativeInstance(IntPtr managedHandle, long jsHandle) =>
WebAssemblyRuntime.InvokeJS($"SkiaSharp.Views.UWP.SKSwapChainPanel.destroyInstance('{jsHandle}')");
object IJSObjectMetadata.InvokeManaged(object instance, string method, string parameters)
{
switch (method)
{
case nameof(RenderFrame):
RenderFrame();
break;
default:
throw new ArgumentException($"Unable to execute method: {method}", nameof(method));
}
return null;
}
}
}
}

Просмотреть файл

@ -22,7 +22,149 @@
return true;
}
}
class SKSwapChainPanel {
static activeInstances = {};
constructor(managedHandle) {
this.managedHandle = managedHandle;
this.canvas = undefined;
this.jsInfo = undefined;
this.renderLoop = false;
this.currentRequest = 0;
}
// JSObject
static createInstance(managedHandle, jsHandle) {
SKSwapChainPanel.activeInstances[jsHandle] = new SKSwapChainPanel(managedHandle);
}
static getInstance(jsHandle) {
return SKSwapChainPanel.activeInstances[jsHandle];
}
static destroyInstance(jsHandle) {
delete SKSwapChainPanel.activeInstances[jsHandle];
}
requestAnimationFrame(renderLoop) {
// optionally update the render loop
if (renderLoop !== undefined && this.renderLoop !== renderLoop)
this.setEnableRenderLoop(renderLoop);
// skip because we have a render loop
if (this.currentRequest !== 0)
return;
// make sure the canvas is scaled correctly for the drawing
this.resizeCanvas();
// add the draw to the next frame
this.currentRequest = window.requestAnimationFrame(() => {
Uno.Foundation.Interop.ManagedObject.dispatch(this.managedHandle, 'RenderFrame', null);
this.currentRequest = 0;
// we may want to draw the next frame
if (this.renderLoop)
this.requestAnimationFrame();
});
}
resizeCanvas() {
if (!this.canvas)
return;
var scale = window.devicePixelRatio || 1;
var w = this.canvas.clientWidth * scale
var h = this.canvas.clientHeight * scale;
if (this.canvas.width !== w)
this.canvas.width = w;
if (this.canvas.height !== h)
this.canvas.height = h;
}
setEnableRenderLoop(enable) {
this.renderLoop = enable;
// either start the new frame or cancel the existing one
if (enable) {
this.requestAnimationFrame();
} else if (this.currentRequest !== 0) {
window.cancelAnimationFrame(this.currentRequest);
this.currentRequest = 0;
}
}
createContext(canvasOrCanvasId) {
if (!canvasOrCanvasId)
throw 'No <canvas> element or ID was provided';
var canvas = canvasOrCanvasId;
if (canvas.tagName !== 'CANVAS') {
canvas = document.getElementById(canvasOrCanvasId);
if (!canvas)
throw `No <canvas> with id ${canvasOrCanvasId} was found`;
}
var ctx = SKSwapChainPanel.createWebGLContext(canvas);
if (!ctx || ctx < 0)
throw `Failed to create WebGL context: err ${ctx}`;
// make current
GL.makeContextCurrent(ctx);
// read values
this.canvas = canvas;
var info = {
ctx: ctx,
fbo: GLctx.getParameter(GLctx.FRAMEBUFFER_BINDING),
stencil: GLctx.getParameter(GLctx.STENCIL_BITS),
sample: 0, // TODO: GLctx.getParameter(GLctx.SAMPLES)
depth: GLctx.getParameter(GLctx.DEPTH_BITS),
};
// format as array for nicer parsing
this.jsInfo = [
info.ctx,
info.fbo ? info.fbo.id : 0,
info.stencil,
info.sample,
info.depth,
];
return this.jsInfo;
}
static createWebGLContext(canvas) {
var contextAttributes = {
alpha: 1,
depth: 1,
stencil: 8,
antialias: 1,
premultipliedAlpha: 1,
preserveDrawingBuffer: 0,
preferLowPowerToHighPerformance: 0,
failIfMajorPerformanceCaveat: 0,
majorVersion: 2,
minorVersion: 0,
enableExtensionsByDefault: 1,
explicitSwapControl: 0,
renderViaOffscreenBackBuffer: 0,
};
var ctx = GL.createContext(canvas, contextAttributes);
if (!ctx && contextAttributes.majorVersion > 1) {
console.warn('Falling back to WebGL 1.0');
contextAttributes.majorVersion = 1;
contextAttributes.minorVersion = 0;
ctx = GL.createContext(canvas, contextAttributes);
}
return ctx;
}
}
UWP.SKXamlCanvas = SKXamlCanvas;
UWP.SKSwapChainPanel = SKSwapChainPanel;
})(UWP = Views.UWP || (Views.UWP = {}));
})(Views = SkiaSharp.Views || (SkiaSharp.Views = {}));

Просмотреть файл

@ -0,0 +1,81 @@
using CoreAnimation;
using Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace SkiaSharp.Views.UWP
{
public partial class SKSwapChainPanel : FrameworkElement
{
private SKGLView glView;
private CADisplayLink displayLink;
public SKSwapChainPanel()
{
Initialize();
}
private SKSize GetCanvasSize() =>
glView?.CanvasSize ?? SKSize.Empty;
private GRContext GetGRContext() =>
glView?.GRContext;
partial void DoLoaded()
{
glView = new SKGLView(Bounds);
glView.PaintSurface += OnPaintSurface;
AddSubview(glView);
}
partial void DoUnloaded()
{
DoEnableRenderLoop(false);
if (glView != null)
{
glView.RemoveFromSuperview();
glView.PaintSurface -= OnPaintSurface;
glView.Dispose();
glView = null;
}
}
private void DoInvalidate() =>
DoEnableRenderLoop(true);
private void OnPaintSurface(object sender, SKPaintGLSurfaceEventArgs e) =>
OnPaintSurface(e);
partial void DoEnableRenderLoop(bool enable)
{
// stop the render loop
if (!enable)
{
if (displayLink != null)
{
displayLink.Invalidate();
displayLink.Dispose();
displayLink = null;
}
return;
}
// only start if we haven't already
if (displayLink != null)
return;
// create the loop
displayLink = CADisplayLink.Create(delegate
{
// redraw the view
glView?.BeginInvokeOnMainThread(() => glView?.Display());
// stop the render loop if it has been disabled or the views are disposed
if (glView == null || !EnableRenderLoop)
DoEnableRenderLoop(false);
});
displayLink.AddToRunLoop(NSRunLoop.Current, NSRunLoop.NSDefaultRunLoopMode);
}
}
}

Просмотреть файл

@ -15,6 +15,7 @@
<ItemGroup>
<Compile Include="..\..\SkiaSharp.Views\SkiaSharp.Views.Shared\**\*.cs" Link="%(RecursiveDir)%(Filename)%(Extension)" />
<Compile Include="..\..\SkiaSharp.Views\SkiaSharp.Views.Apple\SKCGSurfaceFactory.cs" />
<Compile Include="..\..\SkiaSharp.Views\SkiaSharp.Views.AppleiOS\SKGLView.cs" />
<Compile Include="..\..\SkiaSharp.Views\SkiaSharp.Views.UWP\UWPExtensions.cs" />
<Compile Include="..\SkiaSharp.Views.Uno\**\*.cs" Link="%(RecursiveDir)%(Filename)%(Extension)" />
</ItemGroup>

Просмотреть файл

@ -0,0 +1,144 @@
using System;
using Uno;
using Windows.ApplicationModel;
using Windows.Graphics.Display;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Data;
namespace SkiaSharp.Views.UWP
{
public partial class SKSwapChainPanel : FrameworkElement
{
private static readonly DependencyProperty ProxyVisibilityProperty =
DependencyProperty.Register(
"ProxyVisibility",
typeof(Visibility),
typeof(SKSwapChainPanel),
new PropertyMetadata(Visibility.Visible, OnVisibilityChanged));
private static bool designMode = DesignMode.DesignModeEnabled;
private bool isVisible = true;
private bool enableRenderLoop = false;
// workaround for https://github.com/mono/SkiaSharp/issues/1118
private int loadUnloadCounter = 0;
private void Initialize()
{
if (designMode)
return;
var display = DisplayInformation.GetForCurrentView();
OnDpiChanged(display);
Loaded += OnLoaded;
Unloaded += OnUnloaded;
SizeChanged += OnSizeChanged;
var binding = new Binding
{
Path = new PropertyPath(nameof(Visibility)),
Source = this
};
SetBinding(ProxyVisibilityProperty, binding);
}
public SKSize CanvasSize => GetCanvasSize();
public GRContext GRContext => GetGRContext();
public double ContentsScale { get; private set; }
[NotImplemented]
public bool DrawInBackground
{
get => throw new NotImplementedException();
set => throw new NotImplementedException();
}
public bool EnableRenderLoop
{
get => enableRenderLoop;
set
{
if (enableRenderLoop != value)
{
enableRenderLoop = value;
DoEnableRenderLoop(enableRenderLoop);
}
}
}
public new void Invalidate()
{
_ = Dispatcher.RunAsync(CoreDispatcherPriority.Normal, DoInvalidate);
}
public event EventHandler<SKPaintGLSurfaceEventArgs> PaintSurface;
protected virtual void OnPaintSurface(SKPaintGLSurfaceEventArgs e)
{
// invoke the event
PaintSurface?.Invoke(this, e);
}
private static void OnVisibilityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is SKSwapChainPanel canvas && e.NewValue is Visibility visibility)
{
canvas.isVisible = visibility == Visibility.Visible;
canvas.DoUpdateBounds();
canvas.Invalidate();
}
}
private void OnDpiChanged(DisplayInformation sender, object args = null)
{
ContentsScale = sender.LogicalDpi / 96.0f;
DoUpdateBounds();
Invalidate();
}
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
{
DoUpdateBounds();
Invalidate();
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
loadUnloadCounter++;
if (loadUnloadCounter != 1)
return;
DoLoaded();
var display = DisplayInformation.GetForCurrentView();
display.DpiChanged += OnDpiChanged;
OnDpiChanged(display);
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
loadUnloadCounter--;
if (loadUnloadCounter != 0)
return;
DoUnloaded();
var display = DisplayInformation.GetForCurrentView();
display.DpiChanged -= OnDpiChanged;
}
partial void DoLoaded();
partial void DoUnloaded();
partial void DoUpdateBounds();
partial void DoEnableRenderLoop(bool enable);
}
}

Просмотреть файл

@ -18,9 +18,18 @@ using EGLContext = Javax.Microedition.Khronos.Egl.EGLContext;
using EGLDisplay = Javax.Microedition.Khronos.Egl.EGLDisplay;
using EGLSurface = Javax.Microedition.Khronos.Egl.EGLSurface;
#if HAS_UNO
namespace SkiaSharp.Views.UWP
#else
namespace SkiaSharp.Views.Android
#endif
{
public class GLTextureView : TextureView, TextureView.ISurfaceTextureListener, View.IOnLayoutChangeListener
#if HAS_UNO
internal
#else
public
#endif
partial class GLTextureView : TextureView, TextureView.ISurfaceTextureListener, View.IOnLayoutChangeListener
{
private const bool EnableLogging = false;

Просмотреть файл

@ -4,9 +4,18 @@ using Android.Content;
using Android.Opengl;
using Android.Util;
#if HAS_UNO
namespace SkiaSharp.Views.UWP
#else
namespace SkiaSharp.Views.Android
#endif
{
public class SKGLTextureView : GLTextureView
#if HAS_UNO
internal
#else
public
#endif
partial class SKGLTextureView : GLTextureView
{
private SKGLTextureViewRenderer renderer;

Просмотреть файл

@ -5,9 +5,18 @@ using Javax.Microedition.Khronos.Opengles;
using EGLConfig = Javax.Microedition.Khronos.Egl.EGLConfig;
#if HAS_UNO
namespace SkiaSharp.Views.UWP
#else
namespace SkiaSharp.Views.Android
#endif
{
public abstract class SKGLTextureViewRenderer : Java.Lang.Object, GLTextureView.IRenderer
#if HAS_UNO
internal
#else
public
#endif
abstract partial class SKGLTextureViewRenderer : Java.Lang.Object, GLTextureView.IRenderer
{
private const SKColorType colorType = SKColorType.Rgba8888;
private const GRSurfaceOrigin surfaceOrigin = GRSurfaceOrigin.BottomLeft;

Просмотреть файл

@ -8,7 +8,9 @@ using GLKit;
using OpenGLES;
using SkiaSharp.Views.GlesInterop;
#if __TVOS__
#if HAS_UNO
namespace SkiaSharp.Views.UWP
#elif __TVOS__
namespace SkiaSharp.Views.tvOS
#elif __IOS__
namespace SkiaSharp.Views.iOS
@ -16,7 +18,12 @@ namespace SkiaSharp.Views.iOS
{
[Register(nameof(SKGLView))]
[DesignTimeVisible(true)]
public class SKGLView : GLKView, IGLKViewDelegate, IComponent
#if HAS_UNO
internal
#else
public
#endif
class SKGLView : GLKView, IGLKViewDelegate, IComponent
{
private const SKColorType colorType = SKColorType.Rgba8888;
private const GRSurfaceOrigin surfaceOrigin = GRSurfaceOrigin.BottomLeft;

Просмотреть файл

@ -5,11 +5,20 @@ using CoreGraphics;
using Foundation;
using SkiaSharp.Views.GlesInterop;
#if HAS_UNO
namespace SkiaSharp.Views.UWP
#else
namespace SkiaSharp.Views.Mac
#endif
{
[Register(nameof(SKGLView))]
[DesignTimeVisible(true)]
public class SKGLView : NSOpenGLView
#if HAS_UNO
internal
#else
public
#endif
partial class SKGLView : NSOpenGLView
{
private const SKColorType colorType = SKColorType.Rgba8888;
private const GRSurfaceOrigin surfaceOrigin = GRSurfaceOrigin.BottomLeft;
@ -88,7 +97,8 @@ namespace SkiaSharp.Views.Mac
base.Reshape();
// get the new surface size
newSize = ConvertSizeToBacking(Bounds.Size).ToSKSize().ToSizeI();
var size = ConvertSizeToBacking(Bounds.Size);
newSize = new SKSizeI((int)size.Width, (int)size.Height);
}
public override void DrawRect(CGRect dirtyRect)

Просмотреть файл

@ -1,4 +1,4 @@
#if !__WATCHOS__ && !HAS_UNO
#if !__WATCHOS__ && !__WASM__
using System.Runtime.InteropServices;
namespace SkiaSharp.Views.GlesInterop