Skip to content

Commit

Permalink
Merge pull request #8 from mizrael/server
Browse files Browse the repository at this point in the history
added server-side support
  • Loading branch information
mizrael authored Sep 25, 2024
2 parents 29bbf1f + 4c3ad0b commit fc37d03
Show file tree
Hide file tree
Showing 8 changed files with 66 additions and 22 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
2 changes: 1 addition & 1 deletion src/Blazorex/Blazorex.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<PackageVersion>1.0.4</PackageVersion>
<PackageVersion>1.1.0</PackageVersion>
<IsPackable>true</IsPackable>
<Authors>davidguida</Authors>
<Product>Blazorex</Product>
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)

Check warning on line 38 in src/Blazorex/RenderContext2D.cs

View workflow job for this annotation

GitHub Actions / Update NuGet packages

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 38 in src/Blazorex/RenderContext2D.cs

View workflow job for this annotation

GitHub Actions / Update NuGet packages

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
{
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)

Check warning on line 46 in src/Blazorex/RenderContext2D.cs

View workflow job for this annotation

GitHub Actions / Update NuGet packages

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 46 in src/Blazorex/RenderContext2D.cs

View workflow job for this annotation

GitHub Actions / Update NuGet packages

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
{
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);

Check warning on line 134 in src/Blazorex/RenderContext2D.cs

View workflow job for this annotation

GitHub Actions / Update NuGet packages

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

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 fc37d03

Please sign in to comment.