Browse Source

render avalonia content.

pull/9028/head
Dan Walmsley 4 years ago
parent
commit
e1a4609233
  1. 4
      samples/ControlCatalog/MainView.xaml
  2. 17
      src/Web/Avalonia.Web/AvaloniaRuntime.cs
  3. 61
      src/Web/Avalonia.Web/AvaloniaView.cs
  4. 111
      src/Web/Avalonia.Web/webapp/modules/avalonia/canvas.ts

4
samples/ControlCatalog/MainView.xaml

@ -4,7 +4,7 @@
xmlns:controls="clr-namespace:ControlSamples;assembly=ControlSamples"
xmlns:models="clr-namespace:ControlCatalog.Models"
xmlns:pages="clr-namespace:ControlCatalog.Pages">
<Border Background="Red">
<Border Background="Red" Height="100" Width="100">
<CheckBox Content="Test" />
</Border>
</UserControl>

17
src/Web/Avalonia.Web/AvaloniaRuntime.cs

@ -31,11 +31,11 @@ public partial class AvaloniaRuntime
}
public static GLInfo InitialiseGL (JSObject canvas)
public static GLInfo InitialiseGL (JSObject canvas, Action renderFrameCallback)
{
InterceptGLObject();
var info = InitGL(canvas, "testCanvas");
var info = InitGL(canvas, "testCanvas", renderFrameCallback);
var glInfo = new GLInfo(
@ -48,14 +48,21 @@ public partial class AvaloniaRuntime
return glInfo;
}
[JSImport("Canvas.requestAnimationFrame", "avalonia.ts")]
public static partial void RequestAnimationFrame(JSObject canvas, bool renderLoop);
[JSImport("Canvas.createCanvas", "avalonia.ts")]
public static partial JSObject CreateCanvas(JSObject container);
[JSImport("Canvas.Foo", "avalonia.ts")]
public static partial void Foo(JSObject canvas);
[JSImport("Canvas.setCanvasSize", "avalonia.ts")]
public static partial void SetCanvasSize(JSObject canvas, int height, int width);
[JSImport("Canvas.initGL", "avalonia.ts")]
private static partial JSObject InitGL(JSObject canvas, string canvasId);
private static partial JSObject InitGL(
JSObject canvas,
string canvasId,
[JSMarshalAs<JSType.Function>]
Action renderFrameCallback);
[JSImport("StorageProvider.isFileApiSupported", "storage.ts")]
public static partial bool IsFileApiSupported();

61
src/Web/Avalonia.Web/AvaloniaView.cs

@ -1,12 +1,6 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices.JavaScript;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Controls.Embedding;
using Avalonia.Controls.Platform;
using Avalonia.Input.TextInput;
@ -14,7 +8,6 @@ using Avalonia.Platform.Storage;
using Avalonia.Rendering.Composition;
using Avalonia.Web.Blazor;
using Avalonia.Web.Interop;
using SkiaSharp;
using static Avalonia.Web.AvaloniaRuntime;
@ -55,23 +48,17 @@ namespace Avalonia.Web
public AvaloniaView()
{
Console.WriteLine("In AvaloniaView");
var div = GetElementById("out");
Console.WriteLine("got div");
var canvas = AvaloniaRuntime.CreateCanvas(div);
var canvas = CreateCanvas(div);
canvas.SetProperty("id", "mycanvas");
_topLevelImpl = new RazorViewTopLevelImpl(this);
_topLevel = new EmbeddableControlRoot(_topLevelImpl);
Console.WriteLine("created toplevel");
_topLevel.Prepare();
Console.WriteLine("Prepped");
_topLevel.Renderer.Start();
InputHelper.SubscribeKeyboardEvents(
@ -87,7 +74,7 @@ namespace Avalonia.Web
if (_useGL)
{
_jsGlInfo = AvaloniaRuntime.InitialiseGL(canvas);
_jsGlInfo = AvaloniaRuntime.InitialiseGL(canvas, OnRenderFrame);
Console.WriteLine("jsglinfo created - init gl");
}
else
@ -120,22 +107,26 @@ namespace Avalonia.Web
// new PixelSize((int)_canvasSize.Width, (int)_canvasSize.Height), _dpi, _interop.PutImageData);
}
// _interop.SetCanvasSize((int)(_canvasSize.Width * _dpi), (int)(_canvasSize.Height * _dpi));
Threading.Dispatcher.UIThread.Post(async () =>
{
//_interop.RequestAnimationFrame(true);
AvaloniaRuntime.SetCanvasSize(canvas, (int)(_canvasSize.Width * _dpi), (int)(_canvasSize.Height * _dpi));
// _sizeWatcher = new SizeWatcherInterop(_avaloniaModule, _htmlCanvas, OnSizeChanged);
// _dpiWatcher = new DpiWatcherInterop(_avaloniaModule, OnDpiChanged);
_topLevelImpl.SetClientSize(_canvasSize, _dpi);
//_sizeWatcher.Start();
});
RequestAnimationFrame(canvas, true);
}
internal void SetSurface(SKColorType colorType, PixelSize size, double scaling, Action<IntPtr, SKSizeI> blitCallback)
private void OnRenderFrame()
{
//_currentSurface = new BlazorSkiaRasterSurface(colorType, size, scaling, blitCallback);
if (_useGL && (_jsGlInfo == null))
{
Console.WriteLine("nothing to render");
return;
}
if (_canvasSize.Width <= 0 || _canvasSize.Height <= 0 || _dpi <= 0)
{
Console.WriteLine("nothing to render");
return;
}
ManualTriggerRenderTimer.Instance.RaiseTick();
}
public Control? Content
@ -158,22 +149,6 @@ namespace Avalonia.Web
//return _storageProvider ?? throw new InvalidOperationException("Blazor View wasn't initialized yet");
}
private void OnRenderFrame()
{
if (_useGL && (_jsGlInfo == null))
{
Console.WriteLine("nothing to render");
return;
}
if (_canvasSize.Width <= 0 || _canvasSize.Height <= 0 || _dpi <= 0)
{
Console.WriteLine("nothing to render");
return;
}
ManualTriggerRenderTimer.Instance.RaiseTick();
}
private void ForceBlit()
{
// Note: this is technically a hack, but it's a kinda unique use case when

111
src/Web/Avalonia.Web/webapp/modules/avalonia/canvas.ts

@ -8,16 +8,16 @@ type SKGLViewInfo = {
depth: number;
}
type SKHtmlCanvasElement = {
SKHtmlCanvas: Canvas | undefined
type CanvasElement = {
Canvas: Canvas | undefined
} & HTMLCanvasElement
export class Canvas {
static elements: Map<string, HTMLCanvasElement>;
//htmlCanvas: HTMLCanvasElement;
htmlCanvas: HTMLCanvasElement;
glInfo?: SKGLViewInfo;
//renderFrameCallback: DotNet.DotNetObject;
renderFrameCallback: () => void;
renderLoopEnabled: boolean = false;
renderLoopRequest: number = 0;
newWidth?: number;
@ -31,17 +31,17 @@ export class Canvas {
return canvas;
}
public static initGL(element: HTMLCanvasElement, elementId: string): SKGLViewInfo | null {
public static initGL(element: HTMLCanvasElement, elementId: string, renderFrameCallback: () => void): SKGLViewInfo | null {
console.log("inside initGL");
var view = Canvas.init(true, element, elementId);
var view = Canvas.init(true, element, elementId, renderFrameCallback);
if (!view || !view.glInfo)
return null;
return view.glInfo;
}
static init(useGL: boolean, element: HTMLCanvasElement, elementId: string): Canvas | null {
var htmlCanvas = element as SKHtmlCanvasElement;
static init(useGL: boolean, element: HTMLCanvasElement, elementId: string, renderFrameCallback: () => void): Canvas | null {
var htmlCanvas = element as CanvasElement;
if (!htmlCanvas) {
console.error(`No canvas element was provided.`);
return null;
@ -51,17 +51,16 @@ export class Canvas {
Canvas.elements = new Map<string, HTMLCanvasElement>();
Canvas.elements.set(elementId, element);
const view = new Canvas(useGL, element);
const view = new Canvas(useGL, element, renderFrameCallback);
htmlCanvas.SKHtmlCanvas = view;
htmlCanvas.Canvas = view;
return view;
}
public constructor(useGL: boolean, element: HTMLCanvasElement) {
//this.htmlCanvas = element;
//this.renderFrameCallback = callback;
public constructor(useGL: boolean, element: HTMLCanvasElement, renderFrameCallback: () => void) {
this.htmlCanvas = element;
this.renderFrameCallback = renderFrameCallback;
if (useGL) {
const ctx = Canvas.createWebGLContext(element);
@ -79,6 +78,7 @@ export class Canvas {
// read values
const fbo = GLctx.getParameter(GLctx.FRAMEBUFFER_BINDING);
this.glInfo = {
context: ctx,
fboId: fbo ? fbo.id : 0,
@ -89,6 +89,88 @@ export class Canvas {
}
}
public setEnableRenderLoop(enable: boolean) {
this.renderLoopEnabled = enable;
// either start the new frame or cancel the existing one
if (enable) {
//console.info(`Enabling render loop with callback ${this.renderFrameCallback._id}...`);
this.requestAnimationFrame();
} else if (this.renderLoopRequest !== 0) {
window.cancelAnimationFrame(this.renderLoopRequest);
this.renderLoopRequest = 0;
}
}
public requestAnimationFrame(renderLoop?: boolean) {
// optionally update the render loop
if (renderLoop !== undefined && this.renderLoopEnabled !== renderLoop)
this.setEnableRenderLoop(renderLoop);
// skip because we have a render loop
if (this.renderLoopRequest !== 0)
return;
// add the draw to the next frame
this.renderLoopRequest = window.requestAnimationFrame(() => {
if (this.glInfo) {
var GL = (globalThis as any).AvaloniaGL;
// make current
GL.makeContextCurrent(this.glInfo.context);
}
if (this.htmlCanvas.width != this.newWidth) {
this.htmlCanvas.width = this.newWidth || 0;
}
if (this.htmlCanvas.height != this.newHeight) {
this.htmlCanvas.height = this.newHeight || 0;
}
this.renderFrameCallback();
this.renderLoopRequest = 0;
// we may want to draw the next frame
if (this.renderLoopEnabled)
this.requestAnimationFrame();
});
}
public setCanvasSize(width: number, height: number) {
this.newWidth = width;
this.newHeight = height;
if (this.htmlCanvas.width != this.newWidth) {
this.htmlCanvas.width = this.newWidth;
}
if (this.htmlCanvas.height != this.newHeight) {
this.htmlCanvas.height = this.newHeight;
}
if (this.glInfo) {
var GL = (globalThis as any).AvaloniaGL;
// make current
GL.makeContextCurrent(this.glInfo.context);
}
}
public static setCanvasSize(element: HTMLCanvasElement, width: number, height: number) {
const htmlCanvas = element as CanvasElement;
if (!htmlCanvas || !htmlCanvas.Canvas)
return;
htmlCanvas.Canvas.setCanvasSize(width, height);
}
public static requestAnimationFrame(element: HTMLCanvasElement, renderLoop?: boolean) {
const htmlCanvas = element as CanvasElement;
if (!htmlCanvas || !htmlCanvas.Canvas)
return;
htmlCanvas.Canvas.requestAnimationFrame(renderLoop);
}
static createWebGLContext(htmlCanvas: HTMLCanvasElement): WebGLRenderingContext | WebGL2RenderingContext {
const contextAttributes = {
alpha: 1,
@ -109,6 +191,7 @@ export class Canvas {
var GL = (globalThis as any).AvaloniaGL;
let ctx: WebGLRenderingContext = GL.createContext(htmlCanvas, contextAttributes);
if (!ctx && contextAttributes.majorVersion > 1) {
console.warn('Falling back to WebGL 1.0');
contextAttributes.majorVersion = 1;

Loading…
Cancel
Save