5 changed files with 190 additions and 103 deletions
@ -0,0 +1,116 @@ |
|||
using System; |
|||
using System.Runtime.InteropServices; |
|||
using System.Threading; |
|||
using Avalonia.Platform; |
|||
using Avalonia.Remote.Protocol.Viewport; |
|||
using PlatformPixelFormat = Avalonia.Platform.PixelFormat; |
|||
using ProtocolPixelFormat = Avalonia.Remote.Protocol.Viewport.PixelFormat; |
|||
|
|||
namespace Avalonia.Controls.Remote.Server |
|||
{ |
|||
internal partial class RemoteServerTopLevelImpl |
|||
{ |
|||
private enum FrameStatus |
|||
{ |
|||
NotRendered, |
|||
Rendered, |
|||
CopiedToMessage |
|||
} |
|||
|
|||
private sealed class Framebuffer |
|||
{ |
|||
public static Framebuffer Empty { get; } = |
|||
new(ProtocolPixelFormat.Rgba8888, default, new Vector(96.0, 96.0)); |
|||
|
|||
private readonly object _dataLock = new(); |
|||
private readonly byte[] _data; // for rendering only
|
|||
private readonly byte[] _dataCopy; // for messages only
|
|||
private FrameStatus _status = FrameStatus.NotRendered; |
|||
|
|||
public Framebuffer(ProtocolPixelFormat format, Size clientSize, Vector dpi) |
|||
{ |
|||
var frameSize = PixelSize.FromSizeWithDpi(clientSize, dpi); |
|||
if (frameSize.Width <= 0 || frameSize.Height <= 0) |
|||
frameSize = PixelSize.Empty; |
|||
|
|||
var bpp = format == ProtocolPixelFormat.Rgb565 ? 2 : 4; |
|||
var stride = frameSize.Width * bpp; |
|||
var dataLength = Math.Max(0, stride * frameSize.Height); |
|||
|
|||
Format = format; |
|||
FrameSize = frameSize; |
|||
ClientSize = clientSize; |
|||
Dpi = dpi; |
|||
|
|||
(Stride, _data, _dataCopy) = dataLength > 0 ? |
|||
(stride, new byte[dataLength], new byte[dataLength]) : |
|||
(0, Array.Empty<byte>(), Array.Empty<byte>()); |
|||
} |
|||
|
|||
public ProtocolPixelFormat Format { get; } |
|||
|
|||
public Size ClientSize { get; } |
|||
|
|||
public Vector Dpi { get; } |
|||
|
|||
public PixelSize FrameSize { get; } |
|||
|
|||
public int Stride { get; } |
|||
|
|||
public FrameStatus GetStatus() |
|||
{ |
|||
lock (_dataLock) |
|||
return _status; |
|||
} |
|||
|
|||
public ILockedFramebuffer Lock(Action onUnlocked) |
|||
{ |
|||
var handle = GCHandle.Alloc(_data, GCHandleType.Pinned); |
|||
Monitor.Enter(_dataLock); |
|||
|
|||
try |
|||
{ |
|||
return new LockedFramebuffer( |
|||
handle.AddrOfPinnedObject(), |
|||
FrameSize, |
|||
Stride, |
|||
Dpi, |
|||
new PlatformPixelFormat((PixelFormatEnum)Format), |
|||
() => |
|||
{ |
|||
handle.Free(); |
|||
Array.Copy(_data, _dataCopy, _data.Length); |
|||
_status = FrameStatus.Rendered; |
|||
Monitor.Exit(_dataLock); |
|||
onUnlocked(); |
|||
}); |
|||
} |
|||
catch |
|||
{ |
|||
handle.Free(); |
|||
Monitor.Exit(_dataLock); |
|||
throw; |
|||
} |
|||
} |
|||
|
|||
/// <remarks>The returned message must be kept around, as it contains a shared buffer.</remarks>
|
|||
public FrameMessage ToMessage(long sequenceId) |
|||
{ |
|||
lock (_dataLock) |
|||
_status = FrameStatus.CopiedToMessage; |
|||
|
|||
return new FrameMessage |
|||
{ |
|||
SequenceId = sequenceId, |
|||
Data = _dataCopy, |
|||
Format = Format, |
|||
Width = FrameSize.Width, |
|||
Height = FrameSize.Height, |
|||
Stride = Stride, |
|||
DpiX = Dpi.X, |
|||
DpiY = Dpi.Y |
|||
}; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue