Skip to content

Commit

Permalink
added server-side support
Browse files Browse the repository at this point in the history
  • Loading branch information
mizrael committed Sep 25, 2024
1 parent cc168e1 commit 60cf3ca
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 21 deletions.
5 changes: 3 additions & 2 deletions samples/Blazorex.Samples.DoomFire/Pages/Home.razor
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
Hidden = true,
Width = _fireWidth,
Height = _fireHeight,
OnCanvasReady = this.OnFireCanvasReady,
OnCanvasReadyAsync = this.OnFireCanvasReady,
OnFrameReady = this.OnFireCanvasFrameReady,
});
_canvasManager.CreateCanvas("main", new CanvasCreationOptions()
Expand All @@ -48,10 +48,11 @@
});
}

private void OnFireCanvasReady(CanvasBase canvas)
private async ValueTask OnFireCanvasReady(CanvasBase canvas)
{
_fireCanvas = canvas;
_fireRenderer = new Services.FireRenderer(canvas.RenderContext, _fireWidth, _fireHeight);
await _fireRenderer.InitAsync();
}

private void OnFireCanvasFrameReady(float timeStamp)
Expand Down
9 changes: 6 additions & 3 deletions samples/Blazorex.Samples.DoomFire/Services/FireRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ public class FireRenderer
{
private readonly IRenderContext _context;

private readonly int _renderTarget;
private int _renderTarget = -1;
private readonly int _width;
private readonly int _height;
private readonly byte fireStartIntensity = 36;
Expand All @@ -22,15 +22,18 @@ public FireRenderer(IRenderContext context, int width, int height)
_width = width;
_height = height;

_renderTarget = _context.CreateImageData(_width, _height);

fireColorData = new byte[_width * _height * 4];

fireData = new byte[_width * _height];
for (int i = 0; i < fireData.Length; i++)
fireData[i] = fireStartIntensity;
}

public async ValueTask InitAsync()
{
_renderTarget = await _context.CreateImageDataAsync(_width, _height);
}

public void Update()
{
for (int column = 0; column < _width; column++)
Expand Down
4 changes: 2 additions & 2 deletions src/Blazorex/CanvasBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender)
var managedInstance = DotNetObjectReference.Create(this);
await JSRuntime.InvokeVoidAsync("Blazorex.initCanvas", Id, managedInstance);

this.RenderContext = new RenderContext2D(Id, this.JSRuntime as IJSInProcessRuntime);
this.RenderContext = new RenderContext2D(Id, this.JSRuntime);

await this.OnCanvasReady.InvokeAsync(this);
}
Expand All @@ -37,7 +37,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender)
public async ValueTask UpdateFrame(float timeStamp)
{
await this.OnFrameReady.InvokeAsync(timeStamp);
this.RenderContext.ProcessBatch();
await this.RenderContext.ProcessBatchAsync();
}

[JSInvokable]
Expand Down
9 changes: 9 additions & 0 deletions src/Blazorex/CanvasCreationOptions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Components;
using System;
using System.Threading.Tasks;

namespace Blazorex;

Expand Down Expand Up @@ -27,6 +28,14 @@ public readonly struct CanvasCreationOptions
/// </summary>
public Action<CanvasBase> OnCanvasReady { get; init; }

/// <summary>
/// async version of <see cref="OnCanvasReady"/>.
/// </summary>
/// <remarks>
/// <see cref="OnCanvasReady"/> will ALWAYS take precedence over this, if both are set.
/// </remarks>
public Func<CanvasBase, ValueTask> OnCanvasReadyAsync { get; init; }

/// <summary>
/// fired at every frame refresh
/// </summary>
Expand Down
12 changes: 11 additions & 1 deletion src/Blazorex/CanvasManager.razor
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,19 @@
<CascadingValue Value=@this>
@foreach (var (name, options) in _names)
{
if(options.OnCanvasReadyAsync is not null && options.OnCanvasReady is not null)
{
throw new InvalidOperationException($"Cannot set both {nameof(options.OnCanvasReady)} and {nameof(options.OnCanvasReadyAsync)}");
}

EventCallback<CanvasBase> onCanvasReady =
options.OnCanvasReadyAsync != null ?
EventCallback.Factory.Create<CanvasBase>(this, async (canvas) => await options.OnCanvasReadyAsync(canvas)) :
EventCallback.Factory.Create<CanvasBase>(this, options.OnCanvasReady);

<div id="@(name)Container" class="blazorex-canvas-container" hidden="@options.Hidden">
<Canvas @ref="_canvases[name]"
OnCanvasReady="@options.OnCanvasReady"
OnCanvasReady="@onCanvasReady"
OnFrameReady="@options.OnFrameReady"
OnMouseMove="@options.OnMouseMove"
OnKeyDown="@options.OnKeyDown"
Expand Down
7 changes: 4 additions & 3 deletions src/Blazorex/IRenderContext.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
using Microsoft.AspNetCore.Components;
using System.Threading.Tasks;

namespace Blazorex
{
public interface IRenderContext
{
internal void ProcessBatch();
internal ValueTask ProcessBatchAsync();

void ClearRect(float x, float y, float width, float height);
void FillRect(float x, float y, float width, float height);
Expand All @@ -18,9 +19,9 @@ void DrawImage(ElementReference imageRef,

void StrokeText(string text, float x, float y, float? maxWidth = null);
void FillText(string text, float x, float y, float? maxWidth = null);
TextMetrics MeasureText(string text);
ValueTask<TextMetrics> MeasureText(string text);

int CreateImageData(int width, int height);
ValueTask<int> CreateImageDataAsync(int width, int height);
void PutImageData(int imageDataId, byte[] data, double x, double y);

void BeginPath();
Expand Down
40 changes: 30 additions & 10 deletions src/Blazorex/RenderContext2D.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,19 @@
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Threading.Tasks;

namespace Blazorex
{
internal class RenderContext2D : IRenderContext
{
private readonly string _id;
private readonly IJSInProcessRuntime _jsRuntime;
private readonly IJSRuntime _jsRuntime;
private readonly IJSInProcessRuntime _inProcessJsRuntime;

private readonly Queue<JsOp> _jsOps = new();

public RenderContext2D(string id, IJSInProcessRuntime jsRuntime)
public RenderContext2D(string id, IJSRuntime jsRuntime)
{
if (string.IsNullOrWhiteSpace(id))
{
Expand All @@ -22,6 +24,7 @@ public RenderContext2D(string id, IJSInProcessRuntime jsRuntime)

_id = id;
_jsRuntime = jsRuntime;
_inProcessJsRuntime = jsRuntime as IJSInProcessRuntime;
}

#region private methods
Expand All @@ -32,23 +35,40 @@ private void Call(string method, params object[] args)
private void SetProperty(string property, object value)
=> _jsOps.Enqueue(JsOp.PropertyCall(property, value));

private async ValueTask InvokeVoid(string identifier, params object?[]? args)
{
if(_inProcessJsRuntime is not null)
_inProcessJsRuntime.InvokeVoid(identifier, args);
else
await _jsRuntime.InvokeVoidAsync(identifier, args);
}

private ValueTask<T> Invoke<T>(string identifier, params object?[]? args)
{
if (_inProcessJsRuntime is not null)
return ValueTask.FromResult(_inProcessJsRuntime.Invoke<T>(identifier, args));
else
return _jsRuntime.InvokeAsync<T>(identifier, args);
}

#endregion private methods

#region public methods

void IRenderContext.ProcessBatch()
async ValueTask IRenderContext.ProcessBatchAsync()
{
var payload = JsonSerializer.Serialize(_jsOps);
_jsOps.Clear();
_jsRuntime.InvokeVoid("Blazorex.processBatch", _id, payload);

await InvokeVoid("Blazorex.processBatch", _id, payload);
}

internal T DirectCall<T>(string method, params object[] args)
internal ValueTask<T> DirectCall<T>(string method, params object[] args)
{
var payload = string.Empty;
if (args is not null && args.Length != 0)
payload = JsonSerializer.Serialize(args);
var result = _jsRuntime.Invoke<T>("Blazorex.directCall", _id, method, payload);
var result = Invoke<T>("Blazorex.directCall", _id, method, payload);
return result;
}

Expand Down Expand Up @@ -107,13 +127,13 @@ public void FillText(string text, float x, float y, float? maxWidth = null)
this.Call("fillText", text, x, y);
}

public int CreateImageData(int width, int height)
=> _jsRuntime.Invoke<int>("Blazorex.createImageData", _id, width, height);
public ValueTask<int> CreateImageDataAsync(int width, int height)
=> Invoke<int>("Blazorex.createImageData", _id, width, height);

public void PutImageData(int imageDataId, byte[] data, double x, double y)
=> _jsRuntime.InvokeVoid("Blazorex.putImageData", _id, imageDataId, data, x, y);
=> InvokeVoid("Blazorex.putImageData", _id, imageDataId, data, x, y);

public TextMetrics MeasureText(string text)
public ValueTask<TextMetrics> MeasureText(string text)
=> DirectCall<TextMetrics>("measureText", text);

public void Translate(float x, float y)
Expand Down

0 comments on commit 60cf3ca

Please sign in to comment.