зеркало из https://github.com/mono/SkiaSharp.git
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:
Родитель
1fa3daddcc
Коммит
b3f5975cd8
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче