46 changed files with 741 additions and 510 deletions
@ -0,0 +1,94 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Avalonia.Animation.Animators; |
|||
using Avalonia.Media; |
|||
|
|||
namespace Avalonia.Animation; |
|||
|
|||
partial class Animation |
|||
{ |
|||
/// <summary>
|
|||
/// Sets the value of the Animator attached property for a setter.
|
|||
/// </summary>
|
|||
/// <param name="setter">The animation setter.</param>
|
|||
/// <param name="value">The property animator value.</param>
|
|||
[Obsolete("CustomAnimatorBase will be removed before 11.0, use InterpolatingAnimator<T>", true)] |
|||
public static void SetAnimator(IAnimationSetter setter, CustomAnimatorBase value) |
|||
{ |
|||
s_animators[setter] = (value.WrapperType, value.CreateWrapper); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the value of the Animator attached property for a setter.
|
|||
/// </summary>
|
|||
/// <param name="setter">The animation setter.</param>
|
|||
/// <param name="value">The property animator value.</param>
|
|||
public static void SetAnimator(IAnimationSetter setter, ICustomAnimator value) |
|||
{ |
|||
s_animators[setter] = (value.WrapperType, value.CreateWrapper); |
|||
} |
|||
|
|||
private readonly static List<(Func<AvaloniaProperty, bool> Condition, Type Animator, Func<IAnimator> Factory)> |
|||
Animators = new() |
|||
{ |
|||
(prop =>(typeof(double).IsAssignableFrom(prop.PropertyType) && typeof(Transform).IsAssignableFrom(prop.OwnerType)), |
|||
typeof(TransformAnimator), () => new TransformAnimator()), |
|||
(prop => typeof(bool).IsAssignableFrom(prop.PropertyType), typeof(BoolAnimator), () => new BoolAnimator()), |
|||
(prop => typeof(byte).IsAssignableFrom(prop.PropertyType), typeof(ByteAnimator), () => new ByteAnimator()), |
|||
(prop => typeof(Int16).IsAssignableFrom(prop.PropertyType), typeof(Int16Animator), () => new Int16Animator()), |
|||
(prop => typeof(Int32).IsAssignableFrom(prop.PropertyType), typeof(Int32Animator), () => new Int32Animator()), |
|||
(prop => typeof(Int64).IsAssignableFrom(prop.PropertyType), typeof(Int64Animator), () => new Int64Animator()), |
|||
(prop => typeof(UInt16).IsAssignableFrom(prop.PropertyType), typeof(UInt16Animator), () => new UInt16Animator()), |
|||
(prop => typeof(UInt32).IsAssignableFrom(prop.PropertyType), typeof(UInt32Animator), () => new UInt32Animator()), |
|||
(prop => typeof(UInt64).IsAssignableFrom(prop.PropertyType), typeof(UInt64Animator), () => new UInt64Animator()), |
|||
(prop => typeof(float).IsAssignableFrom(prop.PropertyType), typeof(FloatAnimator), () => new FloatAnimator()), |
|||
(prop => typeof(double).IsAssignableFrom(prop.PropertyType), typeof(DoubleAnimator), () => new DoubleAnimator()), |
|||
(prop => typeof(decimal).IsAssignableFrom(prop.PropertyType), typeof(DecimalAnimator), () => new DecimalAnimator()), |
|||
}; |
|||
|
|||
static Animation() |
|||
{ |
|||
RegisterAnimator<IEffect?, EffectAnimator>(); |
|||
RegisterAnimator<BoxShadow, BoxShadowAnimator>(); |
|||
RegisterAnimator<BoxShadows, BoxShadowsAnimator>(); |
|||
RegisterAnimator<IBrush?, BaseBrushAnimator>(); |
|||
RegisterAnimator<CornerRadius, CornerRadiusAnimator>(); |
|||
RegisterAnimator<Color, ColorAnimator>(); |
|||
RegisterAnimator<Vector, VectorAnimator>(); |
|||
RegisterAnimator<Point, PointAnimator>(); |
|||
RegisterAnimator<Rect, RectAnimator>(); |
|||
RegisterAnimator<RelativePoint, RelativePointAnimator>(); |
|||
RegisterAnimator<Size, SizeAnimator>(); |
|||
RegisterAnimator<Thickness, ThicknessAnimator>(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Registers a <see cref="Animator{T}"/> that can handle
|
|||
/// a value type that matches the specified condition.
|
|||
/// </summary>
|
|||
static void RegisterAnimator<T, TAnimator>() |
|||
where TAnimator : Animator<T>, new() |
|||
{ |
|||
Animators.Insert(0, |
|||
(prop => typeof(T).IsAssignableFrom(prop.PropertyType), typeof(TAnimator), () => new TAnimator())); |
|||
} |
|||
|
|||
public static void RegisterCustomAnimator<T, TAnimator>() where TAnimator : InterpolatingAnimator<T>, new() |
|||
{ |
|||
Animators.Insert(0, (prop => typeof(T).IsAssignableFrom(prop.PropertyType), |
|||
typeof(InterpolatingAnimator<T>.AnimatorWrapper), () => new TAnimator().CreateWrapper())); |
|||
} |
|||
|
|||
private static (Type Type, Func<IAnimator> Factory)? GetAnimatorType(AvaloniaProperty property) |
|||
{ |
|||
foreach (var (condition, type, factory) in Animators) |
|||
{ |
|||
if (condition(property)) |
|||
{ |
|||
return (type, factory); |
|||
} |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
} |
|||
@ -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, 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 |
|||
}; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue