mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-12 13:28:33 +00:00
Merge remote-tracking branch 'origin/RGB.NET_v3' into development
This commit is contained in:
commit
7691af95b9
@ -39,8 +39,9 @@
|
||||
<PackageReference Include="DryIoc.dll" />
|
||||
<PackageReference Include="EmbedIO" />
|
||||
<PackageReference Include="HidSharp" />
|
||||
<PackageReference Include="HPPH.SkiaSharp" />
|
||||
<PackageReference Include="Humanizer.Core" />
|
||||
<PackageReference Include="JetBrains.Annotations" PrivateAssets="All"/>
|
||||
<PackageReference Include="JetBrains.Annotations" PrivateAssets="All" />
|
||||
<PackageReference Include="McMaster.NETCore.Plugins" />
|
||||
<PackageReference Include="RGB.NET.Core" />
|
||||
<PackageReference Include="RGB.NET.Layout" />
|
||||
|
||||
@ -1,167 +0,0 @@
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Artemis.Core.ColorScience;
|
||||
|
||||
internal readonly struct ColorRanges
|
||||
{
|
||||
public readonly byte RedRange;
|
||||
public readonly byte GreenRange;
|
||||
public readonly byte BlueRange;
|
||||
|
||||
public ColorRanges(byte redRange, byte greenRange, byte blueRange)
|
||||
{
|
||||
this.RedRange = redRange;
|
||||
this.GreenRange = greenRange;
|
||||
this.BlueRange = blueRange;
|
||||
}
|
||||
}
|
||||
|
||||
internal readonly struct ColorCube
|
||||
{
|
||||
private const int BYTES_PER_COLOR = 4;
|
||||
private static readonly int ELEMENTS_PER_VECTOR = Vector<byte>.Count / BYTES_PER_COLOR;
|
||||
private static readonly int BYTES_PER_VECTOR = ELEMENTS_PER_VECTOR * BYTES_PER_COLOR;
|
||||
|
||||
private readonly int _from;
|
||||
private readonly int _length;
|
||||
private readonly SortTarget _currentOrder = SortTarget.None;
|
||||
|
||||
public ColorCube(in Span<SKColor> fullColorList, int from, int length, SortTarget preOrdered)
|
||||
{
|
||||
this._from = from;
|
||||
this._length = length;
|
||||
|
||||
if (length < 2) return;
|
||||
|
||||
Span<SKColor> colors = fullColorList.Slice(from, length);
|
||||
ColorRanges colorRanges = GetColorRanges(colors);
|
||||
|
||||
if ((colorRanges.RedRange > colorRanges.GreenRange) && (colorRanges.RedRange > colorRanges.BlueRange))
|
||||
{
|
||||
if (preOrdered != SortTarget.Red)
|
||||
QuantizerSort.SortRed(colors);
|
||||
|
||||
_currentOrder = SortTarget.Red;
|
||||
}
|
||||
else if (colorRanges.GreenRange > colorRanges.BlueRange)
|
||||
{
|
||||
if (preOrdered != SortTarget.Green)
|
||||
QuantizerSort.SortGreen(colors);
|
||||
|
||||
_currentOrder = SortTarget.Green;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (preOrdered != SortTarget.Blue)
|
||||
QuantizerSort.SortBlue(colors);
|
||||
|
||||
_currentOrder = SortTarget.Blue;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private ColorRanges GetColorRanges(in ReadOnlySpan<SKColor> colors)
|
||||
{
|
||||
if (Vector.IsHardwareAccelerated && (colors.Length >= Vector<byte>.Count))
|
||||
{
|
||||
int chunks = colors.Length / ELEMENTS_PER_VECTOR;
|
||||
int vectorElements = (chunks * ELEMENTS_PER_VECTOR);
|
||||
int missingElements = colors.Length - vectorElements;
|
||||
|
||||
Vector<byte> max = Vector<byte>.Zero;
|
||||
Vector<byte> min = new(byte.MaxValue);
|
||||
foreach (Vector<byte> currentVector in MemoryMarshal.Cast<SKColor, Vector<byte>>(colors[..vectorElements]))
|
||||
{
|
||||
max = Vector.Max(max, currentVector);
|
||||
min = Vector.Min(min, currentVector);
|
||||
}
|
||||
|
||||
byte redMin = byte.MaxValue;
|
||||
byte redMax = byte.MinValue;
|
||||
byte greenMin = byte.MaxValue;
|
||||
byte greenMax = byte.MinValue;
|
||||
byte blueMin = byte.MaxValue;
|
||||
byte blueMax = byte.MinValue;
|
||||
|
||||
for (int i = 0; i < BYTES_PER_VECTOR; i += BYTES_PER_COLOR)
|
||||
{
|
||||
if (min[i + 2] < redMin) redMin = min[i + 2];
|
||||
if (max[i + 2] > redMax) redMax = max[i + 2];
|
||||
if (min[i + 1] < greenMin) greenMin = min[i + 1];
|
||||
if (max[i + 1] > greenMax) greenMax = max[i + 1];
|
||||
if (min[i] < blueMin) blueMin = min[i];
|
||||
if (max[i] > blueMax) blueMax = max[i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < missingElements; i++)
|
||||
{
|
||||
SKColor color = colors[^(i + 1)];
|
||||
|
||||
if (color.Red < redMin) redMin = color.Red;
|
||||
if (color.Red > redMax) redMax = color.Red;
|
||||
if (color.Green < greenMin) greenMin = color.Green;
|
||||
if (color.Green > greenMax) greenMax = color.Green;
|
||||
if (color.Blue < blueMin) blueMin = color.Blue;
|
||||
if (color.Blue > blueMax) blueMax = color.Blue;
|
||||
}
|
||||
|
||||
return new ColorRanges((byte)(redMax - redMin), (byte)(greenMax - greenMin), (byte)(blueMax - blueMin));
|
||||
}
|
||||
else
|
||||
{
|
||||
byte redMin = byte.MaxValue;
|
||||
byte redMax = byte.MinValue;
|
||||
byte greenMin = byte.MaxValue;
|
||||
byte greenMax = byte.MinValue;
|
||||
byte blueMin = byte.MaxValue;
|
||||
byte blueMax = byte.MinValue;
|
||||
|
||||
foreach (SKColor color in colors)
|
||||
{
|
||||
if (color.Red < redMin) redMin = color.Red;
|
||||
if (color.Red > redMax) redMax = color.Red;
|
||||
if (color.Green < greenMin) greenMin = color.Green;
|
||||
if (color.Green > greenMax) greenMax = color.Green;
|
||||
if (color.Blue < blueMin) blueMin = color.Blue;
|
||||
if (color.Blue > blueMax) blueMax = color.Blue;
|
||||
}
|
||||
|
||||
return new ColorRanges((byte)(redMax - redMin), (byte)(greenMax - greenMin), (byte)(blueMax - blueMin));
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal void Split(in Span<SKColor> fullColorList, out ColorCube a, out ColorCube b)
|
||||
{
|
||||
Span<SKColor> colors = fullColorList.Slice(_from, _length);
|
||||
|
||||
int median = colors.Length / 2;
|
||||
|
||||
a = new ColorCube(fullColorList, _from, median, _currentOrder);
|
||||
b = new ColorCube(fullColorList, _from + median, colors.Length - median, _currentOrder);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal SKColor GetAverageColor(in ReadOnlySpan<SKColor> fullColorList)
|
||||
{
|
||||
ReadOnlySpan<SKColor> colors = fullColorList.Slice(_from, _length);
|
||||
|
||||
int r = 0, g = 0, b = 0;
|
||||
foreach (SKColor color in colors)
|
||||
{
|
||||
r += color.Red;
|
||||
g += color.Green;
|
||||
b += color.Blue;
|
||||
}
|
||||
|
||||
return new SKColor(
|
||||
(byte)(r / colors.Length),
|
||||
(byte)(g / colors.Length),
|
||||
(byte)(b / colors.Length)
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,9 @@
|
||||
using SkiaSharp;
|
||||
using HPPH;
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Artemis.Core.ColorScience;
|
||||
|
||||
@ -16,7 +18,7 @@ public static class ColorQuantizer
|
||||
/// <param name="colors">The colors to quantize</param>
|
||||
/// <param name="amount">How many colors to return. Must be a power of two.</param>
|
||||
/// <returns><paramref name="amount"/> colors.</returns>
|
||||
public static SKColor[] Quantize(in Span<SKColor> colors, int amount)
|
||||
public static SKColor[] Quantize(Span<SKColor> colors, int amount)
|
||||
{
|
||||
if (!BitOperations.IsPow2(amount))
|
||||
throw new ArgumentException("Must be power of two", nameof(amount));
|
||||
@ -31,31 +33,12 @@ public static class ColorQuantizer
|
||||
/// <param name="colors">The colors to quantize</param>
|
||||
/// <param name="splits">How many splits to execute. Each split doubles the number of colors returned.</param>
|
||||
/// <returns>Up to (2 ^ <paramref name="splits"/>) number of colors.</returns>
|
||||
public static SKColor[] QuantizeSplit(in Span<SKColor> colors, int splits)
|
||||
public static SKColor[] QuantizeSplit(Span<SKColor> colors, int splits)
|
||||
{
|
||||
if (colors.Length < (1 << splits)) throw new ArgumentException($"The color array must at least contain ({(1 << splits)}) to perform {splits} splits.");
|
||||
|
||||
Span<ColorCube> cubes = new ColorCube[1 << splits];
|
||||
cubes[0] = new ColorCube(colors, 0, colors.Length, SortTarget.None);
|
||||
|
||||
int currentIndex = 0;
|
||||
for (int i = 0; i < splits; i++)
|
||||
{
|
||||
int currentCubeCount = 1 << i;
|
||||
Span<ColorCube> currentCubes = cubes.Slice(0, currentCubeCount);
|
||||
for (int j = 0; j < currentCubes.Length; j++)
|
||||
{
|
||||
currentCubes[j].Split(colors, out ColorCube a, out ColorCube b);
|
||||
currentCubes[j] = a;
|
||||
cubes[++currentIndex] = b;
|
||||
}
|
||||
}
|
||||
|
||||
SKColor[] result = new SKColor[cubes.Length];
|
||||
for (int i = 0; i < cubes.Length; i++)
|
||||
result[i] = cubes[i].GetAverageColor(colors);
|
||||
|
||||
return result;
|
||||
// DarthAffe 22.07.2024: This is not ideal as it allocates an additional array, but i don't see a way to get SKColors out here
|
||||
return MemoryMarshal.Cast<ColorBGRA, SKColor>(MemoryMarshal.Cast<SKColor, ColorBGRA>(colors).CreateSimpleColorPalette(1 << splits)).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -1,121 +0,0 @@
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
|
||||
namespace Artemis.Core.ColorScience;
|
||||
|
||||
//HACK DarthAffe 17.11.2022: Sorting is a really hot path in the quantizer, therefore abstracting this into cleaner code (one method with parameter or something like that) sadly has a well measurable performance impact.
|
||||
internal static class QuantizerSort
|
||||
{
|
||||
#region Methods
|
||||
|
||||
public static void SortRed(in Span<SKColor> colors)
|
||||
{
|
||||
Span<int> counts = stackalloc int[256];
|
||||
foreach (SKColor t in colors)
|
||||
counts[t.Red]++;
|
||||
|
||||
SKColor[] bucketsArray = ArrayPool<SKColor>.Shared.Rent(colors.Length);
|
||||
|
||||
try
|
||||
{
|
||||
Span<SKColor> buckets = bucketsArray.AsSpan().Slice(0, colors.Length);
|
||||
Span<int> currentBucketIndex = stackalloc int[256];
|
||||
|
||||
int offset = 0;
|
||||
for (int i = 0; i < counts.Length; i++)
|
||||
{
|
||||
currentBucketIndex[i] = offset;
|
||||
offset += counts[i];
|
||||
}
|
||||
|
||||
foreach (SKColor color in colors)
|
||||
{
|
||||
int index = color.Red;
|
||||
int bucketIndex = currentBucketIndex[index];
|
||||
currentBucketIndex[index]++;
|
||||
buckets[bucketIndex] = color;
|
||||
}
|
||||
|
||||
buckets.CopyTo(colors);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<SKColor>.Shared.Return(bucketsArray);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SortGreen(in Span<SKColor> colors)
|
||||
{
|
||||
Span<int> counts = stackalloc int[256];
|
||||
foreach (SKColor t in colors)
|
||||
counts[t.Green]++;
|
||||
|
||||
SKColor[] bucketsArray = ArrayPool<SKColor>.Shared.Rent(colors.Length);
|
||||
|
||||
try
|
||||
{
|
||||
Span<SKColor> buckets = bucketsArray.AsSpan().Slice(0, colors.Length);
|
||||
Span<int> currentBucketIndex = stackalloc int[256];
|
||||
|
||||
int offset = 0;
|
||||
for (int i = 0; i < counts.Length; i++)
|
||||
{
|
||||
currentBucketIndex[i] = offset;
|
||||
offset += counts[i];
|
||||
}
|
||||
|
||||
foreach (SKColor color in colors)
|
||||
{
|
||||
int index = color.Green;
|
||||
int bucketIndex = currentBucketIndex[index];
|
||||
currentBucketIndex[index]++;
|
||||
buckets[bucketIndex] = color;
|
||||
}
|
||||
|
||||
buckets.CopyTo(colors);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<SKColor>.Shared.Return(bucketsArray);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SortBlue(in Span<SKColor> colors)
|
||||
{
|
||||
Span<int> counts = stackalloc int[256];
|
||||
foreach (SKColor t in colors)
|
||||
counts[t.Blue]++;
|
||||
|
||||
SKColor[] bucketsArray = ArrayPool<SKColor>.Shared.Rent(colors.Length);
|
||||
|
||||
try
|
||||
{
|
||||
Span<SKColor> buckets = bucketsArray.AsSpan().Slice(0, colors.Length);
|
||||
Span<int> currentBucketIndex = stackalloc int[256];
|
||||
|
||||
int offset = 0;
|
||||
for (int i = 0; i < counts.Length; i++)
|
||||
{
|
||||
currentBucketIndex[i] = offset;
|
||||
offset += counts[i];
|
||||
}
|
||||
|
||||
foreach (SKColor color in colors)
|
||||
{
|
||||
int index = color.Blue;
|
||||
int bucketIndex = currentBucketIndex[index];
|
||||
currentBucketIndex[index]++;
|
||||
buckets[bucketIndex] = color;
|
||||
}
|
||||
|
||||
buckets.CopyTo(colors);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<SKColor>.Shared.Return(bucketsArray);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
namespace Artemis.Core.ColorScience;
|
||||
|
||||
internal enum SortTarget
|
||||
{
|
||||
None, Red, Green, Blue
|
||||
}
|
||||
@ -1,10 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using Artemis.Core.SkiaSharp;
|
||||
using HPPH;
|
||||
using HPPH.SkiaSharp;
|
||||
using RGB.NET.Core;
|
||||
using RGB.NET.Presets.Textures.Sampler;
|
||||
using RGB.NET.Presets.Extensions;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.Core;
|
||||
@ -12,35 +12,29 @@ namespace Artemis.Core;
|
||||
/// <summary>
|
||||
/// Represents a SkiaSharp-based RGB.NET PixelTexture
|
||||
/// </summary>
|
||||
public sealed class SKTexture : PixelTexture<byte>, IDisposable
|
||||
public sealed class SKTexture : ITexture, IDisposable
|
||||
{
|
||||
private readonly Dictionary<Led, SKRectI> _ledRects;
|
||||
private readonly SKPixmap _pixelData;
|
||||
private readonly IntPtr _pixelDataPtr;
|
||||
|
||||
#region Constructors
|
||||
|
||||
internal SKTexture(IManagedGraphicsContext? graphicsContext, int width, int height, float scale, IReadOnlyCollection<ArtemisDevice> devices) : base(width, height, DATA_PER_PIXEL,
|
||||
new AverageByteSampler())
|
||||
internal SKTexture(IManagedGraphicsContext? graphicsContext, int width, int height, float scale, IReadOnlyCollection<ArtemisDevice> devices)
|
||||
{
|
||||
RenderScale = scale;
|
||||
Size = new Size(width, height);
|
||||
|
||||
ImageInfo = new SKImageInfo(width, height);
|
||||
Surface = graphicsContext == null
|
||||
? SKSurface.Create(ImageInfo)
|
||||
: SKSurface.Create(graphicsContext.GraphicsContext, true, ImageInfo);
|
||||
RenderScale = scale;
|
||||
_pixelDataPtr = Marshal.AllocHGlobal(ImageInfo.BytesSize);
|
||||
_pixelData = new SKPixmap(ImageInfo, _pixelDataPtr, ImageInfo.RowBytes);
|
||||
|
||||
_ledRects = new Dictionary<Led, SKRectI>();
|
||||
foreach (ArtemisDevice artemisDevice in devices)
|
||||
{
|
||||
foreach (ArtemisLed artemisLed in artemisDevice.Leds)
|
||||
{
|
||||
_ledRects[artemisLed.RgbLed] = SKRectI.Create(
|
||||
(int) (artemisLed.AbsoluteRectangle.Left * RenderScale),
|
||||
(int) (artemisLed.AbsoluteRectangle.Top * RenderScale),
|
||||
(int) (artemisLed.AbsoluteRectangle.Width * RenderScale),
|
||||
(int) (artemisLed.AbsoluteRectangle.Height * RenderScale)
|
||||
(int)(artemisLed.AbsoluteRectangle.Left * RenderScale),
|
||||
(int)(artemisLed.AbsoluteRectangle.Top * RenderScale),
|
||||
(int)(artemisLed.AbsoluteRectangle.Width * RenderScale),
|
||||
(int)(artemisLed.AbsoluteRectangle.Height * RenderScale)
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -50,47 +44,29 @@ public sealed class SKTexture : PixelTexture<byte>, IDisposable
|
||||
|
||||
internal Color GetColorAtRenderTarget(in RenderTarget renderTarget)
|
||||
{
|
||||
if (Data.Length == 0) return Color.Transparent;
|
||||
if (_image == null) return Color.Transparent;
|
||||
|
||||
SKRectI skRectI = _ledRects[renderTarget.Led];
|
||||
|
||||
if (skRectI.Width <= 0 || skRectI.Height <= 0)
|
||||
return Color.Transparent;
|
||||
|
||||
SamplerInfo<byte> samplerInfo = new(skRectI.Left, skRectI.Top, skRectI.Width, skRectI.Height, Stride, DataPerPixel, Data);
|
||||
|
||||
Span<byte> pixelData = stackalloc byte[DATA_PER_PIXEL];
|
||||
Sampler.Sample(samplerInfo, pixelData);
|
||||
|
||||
return GetColor(pixelData);
|
||||
}
|
||||
|
||||
private void ReleaseUnmanagedResources()
|
||||
{
|
||||
Marshal.FreeHGlobal(_pixelDataPtr);
|
||||
return _image[skRectI.Left, skRectI.Top, skRectI.Width, skRectI.Height].Average().ToColor();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
~SKTexture()
|
||||
{
|
||||
ReleaseUnmanagedResources();
|
||||
Dispose();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Surface.Dispose();
|
||||
_pixelData.Dispose();
|
||||
|
||||
ReleaseUnmanagedResources();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
#region Constants
|
||||
|
||||
private const int DATA_PER_PIXEL = 4;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
@ -104,20 +80,23 @@ public sealed class SKTexture : PixelTexture<byte>, IDisposable
|
||||
internal void CopyPixelData()
|
||||
{
|
||||
using SKImage skImage = Surface.Snapshot();
|
||||
skImage.ReadPixels(_pixelData);
|
||||
_image = skImage.ToImage();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override Color GetColor(in ReadOnlySpan<byte> pixel) => new(pixel[2], pixel[1], pixel[0]);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Color this[in Rectangle rectangle] => Color.Transparent;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties & Fields
|
||||
|
||||
private IImage? _image;
|
||||
private readonly Dictionary<Led, SKRectI> _ledRects = [];
|
||||
|
||||
/// <inheritdoc />
|
||||
public Size Size { get; }
|
||||
/// <inheritdoc />
|
||||
public Color this[Point point] => Color.Transparent;
|
||||
/// <inheritdoc />
|
||||
public Color this[Rectangle rectangle] => Color.Transparent;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the SKBitmap backing this texture
|
||||
/// </summary>
|
||||
@ -128,11 +107,6 @@ public sealed class SKTexture : PixelTexture<byte>, IDisposable
|
||||
/// </summary>
|
||||
public SKImageInfo ImageInfo { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the color data in RGB format
|
||||
/// </summary>
|
||||
protected override ReadOnlySpan<byte> Data => _pixelData.GetPixelSpan();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the render scale of the texture
|
||||
/// </summary>
|
||||
|
||||
@ -20,7 +20,7 @@ internal class SKTextureBrush : AbstractBrush
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Color GetColorAtPoint(in Rectangle rectangle, in RenderTarget renderTarget)
|
||||
protected override Color GetColorAtPoint(Rectangle rectangle, RenderTarget renderTarget)
|
||||
{
|
||||
return Texture?.GetColorAtRenderTarget(renderTarget) ?? Color.Transparent;
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
<PackageVersion Include="Avalonia.ReactiveUI" Version="11.1.3" />
|
||||
<PackageVersion Include="Avalonia.Skia.Lottie" Version="11.0.0" />
|
||||
<PackageVersion Include="Avalonia.Win32" Version="11.1.3" />
|
||||
<PackageVersion Include="HPPH.SkiaSharp" Version="1.0.0" />
|
||||
<PackageVersion Include="Microsoft.Win32.SystemEvents" Version="8.0.0" />
|
||||
<PackageVersion Include="Avalonia.Xaml.Behaviors" Version="11.1.0.4" />
|
||||
<PackageVersion Include="AvaloniaEdit.TextMate" Version="11.1.0" />
|
||||
@ -44,9 +45,9 @@
|
||||
<PackageVersion Include="NoStringEvaluating" Version="2.5.2" />
|
||||
<PackageVersion Include="Octopus.Octodiff" Version="2.0.546" />
|
||||
<PackageVersion Include="PropertyChanged.SourceGenerator" Version="1.1.0" />
|
||||
<PackageVersion Include="RGB.NET.Core" Version="2.1.0" />
|
||||
<PackageVersion Include="RGB.NET.Layout" Version="2.1.0" />
|
||||
<PackageVersion Include="RGB.NET.Presets" Version="2.1.0" />
|
||||
<PackageVersion Include="RGB.NET.Core" Version="3.0.0-prerelease.1 " />
|
||||
<PackageVersion Include="RGB.NET.Layout" Version="3.0.0-prerelease.1 " />
|
||||
<PackageVersion Include="RGB.NET.Presets" Version="3.0.0-prerelease.1 " />
|
||||
<PackageVersion Include="RawInput.Sharp" Version="0.1.3" />
|
||||
<PackageVersion Include="ReactiveUI" Version="20.1.1" />
|
||||
<PackageVersion Include="ReactiveUI.Validation" Version="4.0.9" />
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user