A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

116 lines
4.0 KiB

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, 1.0);
private readonly double _dpi;
private readonly PixelSize _frameSize;
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, double renderScaling)
{
var frameSize = PixelSize.FromSize(clientSize, renderScaling);
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);
_dpi = renderScaling * 96.0;
_frameSize = frameSize;
Format = format;
ClientSize = clientSize;
RenderScaling = renderScaling;
(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 double RenderScaling { 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,
new Vector(_dpi, _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 NOT 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,
DpiY = _dpi
};
}
}
}
}