1
0
mirror of https://github.com/DarthAffe/RGB.NET.git synced 2025-12-13 01:58:30 +00:00

Merge pull request #183 from DarthAffe/Core/Rendering

Rewrites/improvements in rendering and device handling
This commit is contained in:
DarthAffe 2021-03-06 00:09:28 +01:00 committed by GitHub
commit d14f272ba9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
183 changed files with 3217 additions and 3252 deletions

View File

@ -1,121 +0,0 @@
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable MemberCanBeProtected.Global
// ReSharper disable ReturnTypeCanBeEnumerable.Global
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
// ReSharper disable UnusedMember.Global
using System;
using RGB.NET.Brushes.Gradients;
using RGB.NET.Core;
namespace RGB.NET.Brushes
{
/// <inheritdoc cref="AbstractBrush" />
/// <inheritdoc cref="IGradientBrush" />
/// <summary>
/// Represents a brush drawing a conical gradient.
/// </summary>
public class ConicalGradientBrush : AbstractBrush, IGradientBrush
{
#region Properties & Fields
private float _origin = (float)Math.Atan2(-1, 0);
/// <summary>
/// Gets or sets the origin (radian-angle) this <see cref="ConicalGradientBrush"/> is drawn to. (default: -π/2)
/// </summary>
public float Origin
{
get => _origin;
set => SetProperty(ref _origin, value);
}
private Point _center = new(0.5, 0.5);
/// <summary>
/// Gets or sets the center <see cref="Point"/> (as percentage in the range [0..1]) of the <see cref="IGradient"/> drawn by this <see cref="ConicalGradientBrush"/>. (default: 0.5, 0.5)
/// </summary>
public Point Center
{
get => _center;
set => SetProperty(ref _center, value);
}
private IGradient? _gradient;
/// <inheritdoc />
/// <summary>
/// Gets or sets the gradient drawn by the brush. If null it will default to full transparent.
/// </summary>
public IGradient? Gradient
{
get => _gradient;
set => SetProperty(ref _gradient, value);
}
#endregion
#region Constructors
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.ConicalGradientBrush" /> class.
/// </summary>
public ConicalGradientBrush()
{ }
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.ConicalGradientBrush" /> class.
/// </summary>
/// <param name="gradient">The <see cref="T:RGB.NET.Brushes.Gradients.IGradient" /> drawn by this <see cref="T:RGB.NET.Brushes.ConicalGradientBrush" />.</param>
public ConicalGradientBrush(IGradient gradient)
{
this.Gradient = gradient;
}
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.ConicalGradientBrush" /> class.
/// </summary>
/// <param name="center">The center <see cref="T:RGB.NET.Core.Point" /> (as percentage in the range [0..1]).</param>
/// <param name="gradient">The <see cref="T:RGB.NET.Brushes.Gradients.IGradient" /> drawn by this <see cref="T:RGB.NET.Brushes.ConicalGradientBrush" />.</param>
public ConicalGradientBrush(Point center, IGradient gradient)
{
this.Center = center;
this.Gradient = gradient;
}
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.ConicalGradientBrush" /> class.
/// </summary>
/// <param name="center">The center <see cref="T:RGB.NET.Core.Point" /> (as percentage in the range [0..1]).</param>
/// <param name="origin">The origin (radian-angle) the <see cref="T:RGB.NET.Core.IBrush" /> is drawn to.</param>
/// <param name="gradient">The <see cref="T:RGB.NET.Brushes.Gradients.IGradient" /> drawn by this <see cref="T:RGB.NET.Brushes.ConicalGradientBrush" />.</param>
public ConicalGradientBrush(Point center, float origin, IGradient gradient)
{
this.Center = center;
this.Origin = origin;
this.Gradient = gradient;
}
#endregion
#region Methods
/// <inheritdoc />
protected override Color GetColorAtPoint(Rectangle rectangle, BrushRenderTarget renderTarget)
{
if (Gradient == null) return Color.Transparent;
double centerX = rectangle.Size.Width * Center.X;
double centerY = rectangle.Size.Height * Center.Y;
double angle = Math.Atan2(renderTarget.Point.Y - centerY, renderTarget.Point.X - centerX) - Origin;
if (angle < 0) angle += Math.PI * 2;
double offset = angle / (Math.PI * 2);
return Gradient.GetColor(offset);
}
#endregion
}
}

View File

@ -1,17 +0,0 @@
using RGB.NET.Brushes.Gradients;
using RGB.NET.Core;
namespace RGB.NET.Brushes
{
/// <inheritdoc />
/// <summary>
/// Represents a basic gradient-brush.
/// </summary>
public interface IGradientBrush : IBrush
{
/// <summary>
/// Gets the <see cref="IGradient"/> used by this <see cref="IGradientBrush"/>.
/// </summary>
IGradient? Gradient { get; }
}
}

View File

@ -1,109 +0,0 @@
// ReSharper disable CollectionNeverUpdated.Global
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable MemberCanBeProtected.Global
// ReSharper disable ReturnTypeCanBeEnumerable.Global
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
// ReSharper disable UnusedMember.Global
using RGB.NET.Brushes.Gradients;
using RGB.NET.Brushes.Helper;
using RGB.NET.Core;
namespace RGB.NET.Brushes
{
/// <inheritdoc cref="AbstractBrush" />
/// <inheritdoc cref="IGradientBrush" />
/// <summary>
/// Represents a brush drawing a linear gradient.
/// </summary>
public class LinearGradientBrush : AbstractBrush, IGradientBrush
{
#region Properties & Fields
private Point _startPoint = new(0, 0.5);
/// <summary>
/// Gets or sets the start <see cref="Point"/> (as percentage in the range [0..1]) of the <see cref="IGradient"/> drawn by this <see cref="LinearGradientBrush"/>. (default: 0.0, 0.5)
/// </summary>
public Point StartPoint
{
get => _startPoint;
set => SetProperty(ref _startPoint, value);
}
private Point _endPoint = new(1, 0.5);
/// <summary>
/// Gets or sets the end <see cref="Point"/> (as percentage in the range [0..1]) of the <see cref="IGradient"/> drawn by this <see cref="LinearGradientBrush"/>. (default: 1.0, 0.5)
/// </summary>
public Point EndPoint
{
get => _endPoint;
set => SetProperty(ref _endPoint, value);
}
private IGradient? _gradient;
/// <inheritdoc />
public IGradient? Gradient
{
get => _gradient;
set => SetProperty(ref _gradient, value);
}
#endregion
#region Constructor
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.LinearGradientBrush" /> class.
/// </summary>
public LinearGradientBrush()
{ }
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.LinearGradientBrush" /> class.
/// </summary>
/// <param name="gradient">The <see cref="T:RGB.NET.Brushes.Gradients.IGradient" /> drawn by this <see cref="T:RGB.NET.Brushes.LinearGradientBrush" />.</param>
public LinearGradientBrush(IGradient gradient)
{
this.Gradient = gradient;
}
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.LinearGradientBrush" /> class.
/// </summary>
/// <param name="startPoint">The start <see cref="T:RGB.NET.Core.Point" /> (as percentage in the range [0..1]).</param>
/// <param name="endPoint">The end <see cref="T:RGB.NET.Core.Point" /> (as percentage in the range [0..1]).</param>
/// <param name="gradient">The <see cref="T:RGB.NET.Brushes.Gradients.IGradient" /> drawn by this <see cref="T:RGB.NET.Brushes.LinearGradientBrush" />.</param>
public LinearGradientBrush(Point startPoint, Point endPoint, IGradient gradient)
{
this.StartPoint = startPoint;
this.EndPoint = endPoint;
this.Gradient = gradient;
}
#endregion
#region Methods
/// <inheritdoc />
/// <summary>
/// Gets the color at an specific point assuming the brush is drawn into the given rectangle.
/// </summary>
/// <param name="rectangle">The rectangle in which the brush should be drawn.</param>
/// <param name="renderTarget">The target (key/point) from which the color should be taken.</param>
/// <returns>The color at the specified point.</returns>
protected override Color GetColorAtPoint(Rectangle rectangle, BrushRenderTarget renderTarget)
{
if (Gradient == null) return Color.Transparent;
Point startPoint = new(StartPoint.X * rectangle.Size.Width, StartPoint.Y * rectangle.Size.Height);
Point endPoint = new(EndPoint.X * rectangle.Size.Width, EndPoint.Y * rectangle.Size.Height);
double offset = GradientHelper.CalculateLinearGradientOffset(startPoint, endPoint, renderTarget.Point);
return Gradient.GetColor(offset);
}
#endregion
}
}

View File

@ -1,97 +0,0 @@
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
// ReSharper disable UnusedMember.Global
using System;
using RGB.NET.Brushes.Gradients;
using RGB.NET.Brushes.Helper;
using RGB.NET.Core;
namespace RGB.NET.Brushes
{
/// <inheritdoc cref="AbstractBrush" />
/// <inheritdoc cref="IGradientBrush" />
/// <summary>
/// Represents a brush drawing a radial gradient around a center point.
/// </summary>
public class RadialGradientBrush : AbstractBrush, IGradientBrush
{
#region Properties & Fields
private Point _center = new(0.5, 0.5);
/// <summary>
/// Gets or sets the center <see cref="Point"/> (as percentage in the range [0..1]) around which the <see cref="RadialGradientBrush"/> should be drawn. (default: 0.5, 0.5)
/// </summary>
public Point Center
{
get => _center;
set => SetProperty(ref _center, value);
}
private IGradient? _gradient;
/// <inheritdoc />
public IGradient? Gradient
{
get => _gradient;
set => SetProperty(ref _gradient, value);
}
#endregion
#region Constructors
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.RadialGradientBrush" /> class.
/// </summary>
public RadialGradientBrush()
{ }
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.RadialGradientBrush" /> class.
/// </summary>
/// <param name="gradient">The gradient drawn by the brush.</param>
public RadialGradientBrush(IGradient gradient)
{
this.Gradient = gradient;
}
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.RadialGradientBrush" /> class.
/// </summary>
/// <param name="center">The center point (as percentage in the range [0..1]).</param>
/// <param name="gradient">The gradient drawn by the brush.</param>
public RadialGradientBrush(Point center, IGradient gradient)
{
this.Center = center;
this.Gradient = gradient;
}
#endregion
#region Methods
/// <inheritdoc />
protected override Color GetColorAtPoint(Rectangle rectangle, BrushRenderTarget renderTarget)
{
if (Gradient == null) return Color.Transparent;
Point centerPoint = new(rectangle.Location.X + (rectangle.Size.Width * Center.X), rectangle.Location.Y + (rectangle.Size.Height * Center.Y));
// Calculate the distance to the farthest point from the center as reference (this has to be a corner)
// ReSharper disable once RedundantCast - never trust this ...
double refDistance = Math.Max(Math.Max(Math.Max(GradientHelper.CalculateDistance(rectangle.Location, centerPoint),
GradientHelper.CalculateDistance(new Point(rectangle.Location.X + rectangle.Size.Width, rectangle.Location.Y), centerPoint)),
GradientHelper.CalculateDistance(new Point(rectangle.Location.X, rectangle.Location.Y + rectangle.Size.Height), centerPoint)),
GradientHelper.CalculateDistance(new Point(rectangle.Location.X + rectangle.Size.Width, rectangle.Location.Y + rectangle.Size.Height), centerPoint));
double distance = GradientHelper.CalculateDistance(renderTarget.Point, centerPoint);
double offset = distance / refDistance;
return Gradient.GetColor(offset);
}
#endregion
}
}

View File

@ -1,56 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net5.0</TargetFrameworks>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<Authors>Darth Affe</Authors>
<Company>Wyrez</Company>
<Language>en-US</Language>
<NeutralLanguage>en-US</NeutralLanguage>
<Title>RGB.NET.Brushes</Title>
<AssemblyName>RGB.NET.Brushes</AssemblyName>
<AssemblyTitle>RGB.NET.Brushes</AssemblyTitle>
<PackageId>RGB.NET.Brushes</PackageId>
<RootNamespace>RGB.NET.Brushes</RootNamespace>
<Description>Brushes-Presets of RGB.NET</Description>
<Summary>Brushes-Presets of RGB.NET, a C# (.NET) library for accessing various RGB-peripherals</Summary>
<Copyright>Copyright © Darth Affe 2020</Copyright>
<PackageCopyright>Copyright © Darth Affe 2020</PackageCopyright>
<PackageIconUrl>http://lib.arge.be/icon.png</PackageIconUrl>
<PackageProjectUrl>https://github.com/DarthAffe/RGB.NET</PackageProjectUrl>
<PackageLicenseUrl>https://raw.githubusercontent.com/DarthAffe/RGB.NET/master/LICENSE</PackageLicenseUrl>
<RepositoryType>Github</RepositoryType>
<RepositoryUrl>https://github.com/DarthAffe/RGB.NET</RepositoryUrl>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<PackageReleaseNotes></PackageReleaseNotes>
<Version>0.0.1</Version>
<AssemblyVersion>0.0.1</AssemblyVersion>
<FileVersion>0.0.1</FileVersion>
<OutputPath>..\bin\</OutputPath>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<IncludeSource>True</IncludeSource>
<IncludeSymbols>True</IncludeSymbols>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<DefineConstants>$(DefineConstants);TRACE;DEBUG</DefineConstants>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<NoWarn>$(NoWarn);CS1591;CS1572;CS1573</NoWarn>
<DefineConstants>$(DefineConstants);RELEASE</DefineConstants>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\RGB.NET.Core\RGB.NET.Core.csproj" />
</ItemGroup>
</Project>

View File

@ -1,3 +0,0 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=brushes/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=decorators/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@ -1,61 +0,0 @@
// ReSharper disable UnusedMemberInSuper.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable ReturnTypeCanBeEnumerable.Global
using System.Collections.Generic;
namespace RGB.NET.Core
{
/// <summary>
/// Represents a basic brush.
/// </summary>
public interface IBrush : IDecoratable<IBrushDecorator>
{
/// <summary>
/// Gets or sets if the <see cref="IBrush"/> is enabled and will be drawn on an update.
/// </summary>
bool IsEnabled { get; set; }
/// <summary>
/// Gets or sets the calculation mode used for the rectangle/points used for color-selection in brushes.
/// </summary>
BrushCalculationMode BrushCalculationMode { get; set; }
/// <summary>
/// Gets or sets the overall percentage brightness of the <see cref="IBrush"/>.
/// </summary>
double Brightness { get; set; }
/// <summary>
/// Gets or sets the overall percentage opacity of the <see cref="IBrush"/>.
/// </summary>
double Opacity { get; set; }
/// <summary>
/// Gets a list of <see cref="IColorCorrection"/> used to correct the colors of the <see cref="IBrush"/>.
/// </summary>
IList<IColorCorrection> ColorCorrections { get; }
/// <summary>
/// Gets the <see cref="RenderedRectangle"/> used in the last render pass.
/// </summary>
Rectangle RenderedRectangle { get; }
/// <summary>
/// Gets a dictionary containing all <see cref="Color"/> for <see cref="BrushRenderTarget"/> calculated in the last render pass.
/// </summary>
Dictionary<BrushRenderTarget, Color> RenderedTargets { get; }
/// <summary>
/// Performs the render pass of the <see cref="IBrush"/> and calculates the raw <see cref="Color"/> for all requested <see cref="BrushRenderTarget"/>.
/// </summary>
/// <param name="rectangle">The <see cref="Rectangle"/> in which the brush should be drawn.</param>
/// <param name="renderTargets">The <see cref="BrushRenderTarget"/> (keys/points) of which the color should be calculated.</param>
void PerformRender(Rectangle rectangle, IEnumerable<BrushRenderTarget> renderTargets);
/// <summary>
/// Performs the finalize pass of the <see cref="IBrush"/> and calculates the final <see cref="ColorCorrections"/> for all previously calculated <see cref="BrushRenderTarget"/>.
/// </summary>
void PerformFinalize();
}
}

View File

@ -8,18 +8,18 @@
/// Converts the individual byte values of this <see cref="Color"/> to a human-readable string.
/// </summary>
/// <returns>A string that contains the individual byte values of this <see cref="Color"/>. For example "[A: 255, R: 255, G: 0, B: 0]".</returns>
public virtual string ToString(Color color) => $"[A: {color.GetA()}, R: {color.GetR()}, G: {color.GetG()}, B: {color.GetB()}]";
public virtual string ToString(in Color color) => $"[A: {color.GetA()}, R: {color.GetR()}, G: {color.GetG()}, B: {color.GetB()}]";
/// <summary>
/// Tests whether the specified object is a <see cref="Color" /> and is equivalent to this <see cref="Color" />.
/// </summary>
/// <param name="obj">The object to test.</param>
/// <returns><c>true</c> if <paramref name="obj" /> is a <see cref="Color" /> equivalent to this <see cref="Color" />; otherwise, <c>false</c>.</returns>
public virtual bool Equals(Color color, object? obj)
public virtual bool Equals(in Color color, object? obj)
{
if (!(obj is Color)) return false;
if (!(obj is Color color2)) return false;
(double a, double r, double g, double b) = ((Color)obj).GetRGB();
(float a, float r, float g, float b) = color2.GetRGB();
return color.A.EqualsInTolerance(a) && color.R.EqualsInTolerance(r) && color.G.EqualsInTolerance(g) && color.B.EqualsInTolerance(b);
}
@ -27,7 +27,7 @@
/// Returns a hash code for this <see cref="Color" />.
/// </summary>
/// <returns>An integer value that specifies the hash code for this <see cref="Color" />.</returns>
public virtual int GetHashCode(Color color)
public virtual int GetHashCode(in Color color)
{
unchecked
{
@ -43,17 +43,17 @@
/// Blends a <see cref="Color"/> over this color.
/// </summary>
/// <param name="color">The <see cref="Color"/> to blend.</param>
public virtual Color Blend(Color baseColor, Color blendColor)
public virtual Color Blend(in Color baseColor, in Color blendColor)
{
if (blendColor.A.EqualsInTolerance(0)) return baseColor;
if (blendColor.A.EqualsInTolerance(1))
return blendColor;
double resultA = (1.0 - ((1.0 - blendColor.A) * (1.0 - baseColor.A)));
double resultR = (((blendColor.R * blendColor.A) / resultA) + ((baseColor.R * baseColor.A * (1.0 - blendColor.A)) / resultA));
double resultG = (((blendColor.G * blendColor.A) / resultA) + ((baseColor.G * baseColor.A * (1.0 - blendColor.A)) / resultA));
double resultB = (((blendColor.B * blendColor.A) / resultA) + ((baseColor.B * baseColor.A * (1.0 - blendColor.A)) / resultA));
float resultA = (1.0f - ((1.0f - blendColor.A) * (1.0f - baseColor.A)));
float resultR = (((blendColor.R * blendColor.A) / resultA) + ((baseColor.R * baseColor.A * (1.0f - blendColor.A)) / resultA));
float resultG = (((blendColor.G * blendColor.A) / resultA) + ((baseColor.G * baseColor.A * (1.0f - blendColor.A)) / resultA));
float resultB = (((blendColor.B * blendColor.A) / resultA) + ((baseColor.B * baseColor.A * (1.0f - blendColor.A)) / resultA));
return new Color(resultA, resultR, resultG, resultB);
}

View File

@ -2,12 +2,12 @@
{
public interface IColorBehavior
{
string ToString(Color color);
string ToString(in Color color);
bool Equals(Color color, object? obj);
bool Equals(in Color color, object? obj);
int GetHashCode(Color color);
int GetHashCode(in Color color);
Color Blend(Color baseColor, Color blendColor);
Color Blend(in Color baseColor, in Color blendColor);
}
}

View File

@ -15,10 +15,11 @@ namespace RGB.NET.Core
{
#region Constants
private static readonly Color TRANSPARENT = new(0, 0, 0, 0);
/// <summary>
/// Gets an transparent color [A: 0, R: 0, G: 0, B: 0]
/// </summary>
public static Color Transparent => new(0, 0, 0, 0);
public static ref readonly Color Transparent => ref TRANSPARENT;
#endregion
@ -32,22 +33,22 @@ namespace RGB.NET.Core
/// <summary>
/// Gets the alpha component value of this <see cref="Color"/> as percentage in the range [0..1].
/// </summary>
public double A { get; }
public float A { get; }
/// <summary>
/// Gets the red component value of this <see cref="Color"/> as percentage in the range [0..1].
/// </summary>
public double R { get; }
public float R { get; }
/// <summary>
/// Gets the green component value of this <see cref="Color"/> as percentage in the range [0..1].
/// </summary>
public double G { get; }
public float G { get; }
/// <summary>
/// Gets the blue component value of this <see cref="Color"/> as percentage in the range [0..1].
/// </summary>
public double B { get; }
public float B { get; }
#endregion
@ -106,8 +107,8 @@ namespace RGB.NET.Core
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
public Color(double r, double g, double b)
: this(1.0, r, g, b)
public Color(float r, float g, float b)
: this(1.0f, r, g, b)
{ }
/// <summary>
@ -117,7 +118,7 @@ namespace RGB.NET.Core
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
public Color(double a, byte r, byte g, byte b)
public Color(float a, byte r, byte g, byte b)
: this(a, r.GetPercentageFromByteValue(), g.GetPercentageFromByteValue(), b.GetPercentageFromByteValue())
{ }
@ -128,7 +129,7 @@ namespace RGB.NET.Core
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
public Color(double a, int r, int g, int b)
public Color(float a, int r, int g, int b)
: this(a, (byte)r.Clamp(0, byte.MaxValue), (byte)g.Clamp(0, byte.MaxValue), (byte)b.Clamp(0, byte.MaxValue))
{ }
@ -139,7 +140,7 @@ namespace RGB.NET.Core
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
public Color(int a, double r, double g, double b)
public Color(int a, float r, float g, float b)
: this((byte)a.Clamp(0, byte.MaxValue), r, g, b)
{ }
@ -150,7 +151,7 @@ namespace RGB.NET.Core
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
public Color(byte a, double r, double g, double b)
public Color(byte a, float r, float g, float b)
: this(a.GetPercentageFromByteValue(), r, g, b)
{ }
@ -161,7 +162,7 @@ namespace RGB.NET.Core
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
public Color(double a, double r, double g, double b)
public Color(float a, float r, float g, float b)
{
A = a.Clamp(0, 1);
R = r.Clamp(0, 1);
@ -174,7 +175,7 @@ namespace RGB.NET.Core
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct by cloning a existing <see cref="T:RGB.NET.Core.Color" />.
/// </summary>
/// <param name="color">The <see cref="T:RGB.NET.Core.Color" /> the values are copied from.</param>
public Color(Color color)
public Color(in Color color)
: this(color.A, color.R, color.G, color.B)
{ }
@ -206,7 +207,7 @@ namespace RGB.NET.Core
/// Blends a <see cref="Color"/> over this color, as defined by the current <see cref="Behavior"/>.
/// </summary>
/// <param name="color">The <see cref="Color"/> to blend.</param>
public Color Blend(Color color) => Behavior.Blend(this, color);
public Color Blend(in Color color) => Behavior.Blend(this, color);
#endregion
@ -218,7 +219,7 @@ namespace RGB.NET.Core
/// <param name="color1">The base color.</param>
/// <param name="color2">The color to blend.</param>
/// <returns>The blended color.</returns>
public static Color operator +(Color color1, Color color2) => color1.Blend(color2);
public static Color operator +(in Color color1, in Color color2) => color1.Blend(color2);
/// <summary>
/// Returns a value that indicates whether two specified <see cref="Color" /> are equal.
@ -226,7 +227,7 @@ namespace RGB.NET.Core
/// <param name="color1">The first <see cref="Color" /> to compare.</param>
/// <param name="color2">The second <see cref="Color" /> to compare.</param>
/// <returns><c>true</c> if <paramref name="color1" /> and <paramref name="color2" /> are equal; otherwise, <c>false</c>.</returns>
public static bool operator ==(Color color1, Color color2) => color1.Equals(color2);
public static bool operator ==(in Color color1, in Color color2) => color1.Equals(color2);
/// <summary>
/// Returns a value that indicates whether two specified <see cref="Color" /> are equal.
@ -234,7 +235,7 @@ namespace RGB.NET.Core
/// <param name="color1">The first <see cref="Color" /> to compare.</param>
/// <param name="color2">The second <see cref="Color" /> to compare.</param>
/// <returns><c>true</c> if <paramref name="color1" /> and <paramref name="color2" /> are not equal; otherwise, <c>false</c>.</returns>
public static bool operator !=(Color color1, Color color2) => !(color1 == color2);
public static bool operator !=(in Color color1, in Color color2) => !(color1 == color2);
/// <summary>
/// Converts a <see cref="ValueTuple"/> of ARGB-components to a <see cref="Color"/>.
@ -269,14 +270,14 @@ namespace RGB.NET.Core
/// </summary>
/// <param name="components">The <see cref="ValueTuple"/> containing the components.</param>
/// <returns>The color.</returns>
public static implicit operator Color((double r, double g, double b) components) => new(components.r, components.g, components.b);
public static implicit operator Color((float r, float g, float b) components) => new(components.r, components.g, components.b);
/// <summary>
/// Converts a <see cref="ValueTuple"/> of ARGB-components to a <see cref="Color"/>.
/// </summary>
/// <param name="components">The <see cref="ValueTuple"/> containing the components.</param>
/// <returns>The color.</returns>
public static implicit operator Color((double a, double r, double g, double b) components) => new(components.a, components.r, components.g, components.b);
public static implicit operator Color((float a, float r, float g, float b) components) => new(components.a, components.r, components.g, components.b);
#endregion
}

View File

@ -13,21 +13,21 @@ namespace RGB.NET.Core
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
public static double GetHue(this Color color) => color.GetHSV().hue;
public static float GetHue(this in Color color) => color.GetHSV().hue;
/// <summary>
/// Gets the saturation component value (HSV-color space) of this <see cref="Color"/> in the range [0..1].
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
public static double GetSaturation(this Color color) => color.GetHSV().saturation;
public static float GetSaturation(this in Color color) => color.GetHSV().saturation;
/// <summary>
/// Gets the value component value (HSV-color space) of this <see cref="Color"/> in the range [0..1].
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
public static double GetValue(this Color color) => color.GetHSV().value;
public static float GetValue(this in Color color) => color.GetHSV().value;
/// <summary>
/// Gets the hue, saturation and value component values (HSV-color space) of this <see cref="Color"/>.
@ -37,7 +37,7 @@ namespace RGB.NET.Core
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
public static (double hue, double saturation, double value) GetHSV(this Color color)
public static (float hue, float saturation, float value) GetHSV(this in Color color)
=> CaclulateHSVFromRGB(color.R, color.G, color.B);
#endregion
@ -51,9 +51,9 @@ namespace RGB.NET.Core
/// <param name="saturation">The saturation value to add.</param>
/// <param name="value">The value value to add.</param>
/// <returns>The new color after the modification.</returns>
public static Color AddHSV(this Color color, double hue = 0, double saturation = 0, double value = 0)
public static Color AddHSV(this in Color color, float hue = 0, float saturation = 0, float value = 0)
{
(double cHue, double cSaturation, double cValue) = color.GetHSV();
(float cHue, float cSaturation, float cValue) = color.GetHSV();
return Create(color.A, cHue + hue, cSaturation + saturation, cValue + value);
}
@ -64,9 +64,9 @@ namespace RGB.NET.Core
/// <param name="saturation">The saturation value to subtract.</param>
/// <param name="value">The value value to subtract.</param>
/// <returns>The new color after the modification.</returns>
public static Color SubtractHSV(this Color color, double hue = 0, double saturation = 0, double value = 0)
public static Color SubtractHSV(this in Color color, float hue = 0, float saturation = 0, float value = 0)
{
(double cHue, double cSaturation, double cValue) = color.GetHSV();
(float cHue, float cSaturation, float cValue) = color.GetHSV();
return Create(color.A, cHue - hue, cSaturation - saturation, cValue - value);
}
@ -77,9 +77,9 @@ namespace RGB.NET.Core
/// <param name="saturation">The saturation value to multiply.</param>
/// <param name="value">The value value to multiply.</param>
/// <returns>The new color after the modification.</returns>
public static Color MultiplyHSV(this Color color, double hue = 1, double saturation = 1, double value = 1)
public static Color MultiplyHSV(this in Color color, float hue = 1, float saturation = 1, float value = 1)
{
(double cHue, double cSaturation, double cValue) = color.GetHSV();
(float cHue, float cSaturation, float cValue) = color.GetHSV();
return Create(color.A, cHue * hue, cSaturation * saturation, cValue * value);
}
@ -90,9 +90,9 @@ namespace RGB.NET.Core
/// <param name="saturation">The saturation value to divide.</param>
/// <param name="value">The value value to divide.</param>
/// <returns>The new color after the modification.</returns>
public static Color DivideHSV(this Color color, double hue = 1, double saturation = 1, double value = 1)
public static Color DivideHSV(this in Color color, float hue = 1, float saturation = 1, float value = 1)
{
(double cHue, double cSaturation, double cValue) = color.GetHSV();
(float cHue, float cSaturation, float cValue) = color.GetHSV();
return Create(color.A, cHue / hue, cSaturation / saturation, cValue / value);
}
@ -103,9 +103,9 @@ namespace RGB.NET.Core
/// <param name="saturation">The saturation value to set.</param>
/// <param name="value">The value value to set.</param>
/// <returns>The new color after the modification.</returns>
public static Color SetHSV(this Color color, double? hue = null, double? saturation = null, double? value = null)
public static Color SetHSV(this in Color color, float? hue = null, float? saturation = null, float? value = null)
{
(double cHue, double cSaturation, double cValue) = color.GetHSV();
(float cHue, float cSaturation, float cValue) = color.GetHSV();
return Create(color.A, hue ?? cHue, saturation ?? cSaturation, value ?? cValue);
}
@ -120,8 +120,8 @@ namespace RGB.NET.Core
/// <param name="saturation">The saturation component value of this <see cref="Color"/>.</param>
/// <param name="value">The value component value of this <see cref="Color"/>.</param>
/// <returns>The color created from the values.</returns>
public static Color Create(double hue, double saturation, double value)
=> Create(1.0, hue, saturation, value);
public static Color Create(float hue, float saturation, float value)
=> Create(1.0f, hue, saturation, value);
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using AHSV-Values.
@ -131,8 +131,8 @@ namespace RGB.NET.Core
/// <param name="saturation">The saturation component value of this <see cref="Color"/>.</param>
/// <param name="value">The value component value of this <see cref="Color"/>.</param>
/// <returns>The color created from the values.</returns>
public static Color Create(byte a, double hue, double saturation, double value)
=> Create((double)a / byte.MaxValue, hue, saturation, value);
public static Color Create(byte a, float hue, float saturation, float value)
=> Create((float)a / byte.MaxValue, hue, saturation, value);
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using AHSV-Values.
@ -142,8 +142,8 @@ namespace RGB.NET.Core
/// <param name="saturation">The saturation component value of this <see cref="Color"/>.</param>
/// <param name="value">The value component value of this <see cref="Color"/>.</param>
/// <returns>The color created from the values.</returns>
public static Color Create(int a, double hue, double saturation, double value)
=> Create((double)a / byte.MaxValue, hue, saturation, value);
public static Color Create(int a, float hue, float saturation, float value)
=> Create((float)a / byte.MaxValue, hue, saturation, value);
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using AHSV-Values.
@ -153,9 +153,9 @@ namespace RGB.NET.Core
/// <param name="saturation">The saturation component value of this <see cref="Color"/>.</param>
/// <param name="value">The value component value of this <see cref="Color"/>.</param>
/// <returns>The color created from the values.</returns>
public static Color Create(double a, double hue, double saturation, double value)
public static Color Create(float a, float hue, float saturation, float value)
{
(double r, double g, double b) = CalculateRGBFromHSV(hue, saturation, value);
(float r, float g, float b) = CalculateRGBFromHSV(hue, saturation, value);
return new Color(a, r, g, b);
}
@ -163,33 +163,33 @@ namespace RGB.NET.Core
#region Helper
private static (double h, double s, double v) CaclulateHSVFromRGB(double r, double g, double b)
private static (float h, float s, float v) CaclulateHSVFromRGB(float r, float g, float b)
{
if (r.EqualsInTolerance(g) && g.EqualsInTolerance(b)) return (0, 0, r);
double min = Math.Min(Math.Min(r, g), b);
double max = Math.Max(Math.Max(r, g), b);
float min = Math.Min(Math.Min(r, g), b);
float max = Math.Max(Math.Max(r, g), b);
double hue;
float hue;
if (max.EqualsInTolerance(min))
hue = 0;
else if (max.EqualsInTolerance(r)) // r is max
hue = (g - b) / (max - min);
else if (max.EqualsInTolerance(g)) // g is max
hue = 2.0 + ((b - r) / (max - min));
hue = 2.0f + ((b - r) / (max - min));
else // b is max
hue = 4.0 + ((r - g) / (max - min));
hue = 4.0f + ((r - g) / (max - min));
hue *= 60.0;
hue *= 60.0f;
hue = hue.Wrap(0, 360);
double saturation = max.EqualsInTolerance(0) ? 0 : 1.0 - (min / max);
double value = Math.Max(r, Math.Max(g, b));
float saturation = max.EqualsInTolerance(0) ? 0 : 1.0f - (min / max);
float value = Math.Max(r, Math.Max(g, b));
return (hue, saturation, value);
}
private static (double r, double g, double b) CalculateRGBFromHSV(double h, double s, double v)
private static (float r, float g, float b) CalculateRGBFromHSV(float h, float s, float v)
{
h = h.Wrap(0, 360);
s = s.Clamp(0, 1);
@ -198,12 +198,12 @@ namespace RGB.NET.Core
if (s <= 0.0)
return (v, v, v);
double hh = h / 60.0;
float hh = h / 60.0f;
int i = (int)hh;
double ff = hh - i;
double p = v * (1.0 - s);
double q = v * (1.0 - (s * ff));
double t = v * (1.0 - (s * (1.0 - ff)));
float ff = hh - i;
float p = v * (1.0f - s);
float q = v * (1.0f - (s * ff));
float t = v * (1.0f - (s * (1.0f - ff)));
return i switch
{

View File

@ -0,0 +1,203 @@
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable UnusedMember.Global
using System;
namespace RGB.NET.Core
{
public static class HclColor
{
#region Getter
/// <summary>
/// Gets the H component value (Hcl-color space) of this <see cref="Color"/> in the range [0..360].
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
public static float GetHclH(this in Color color) => color.GetHcl().l;
/// <summary>
/// Gets the c component value (Hcl-color space) of this <see cref="Color"/> in the range [0..1].
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
public static float GetHclC(this in Color color) => color.GetHcl().c;
/// <summary>
/// Gets the l component value (Hcl-color space) of this <see cref="Color"/> in the range [0..1].
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
public static float GetHclL(this in Color color) => color.GetHcl().h;
/// <summary>
/// Gets the H, c and l component values (Hcl-color space) of this <see cref="Color"/>.
/// H in the range [0..360].
/// c in the range [0..1].
/// l in the range [0..1].
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
public static (float h, float c, float l) GetHcl(this in Color color)
=> CalculateHclFromRGB(color.R, color.G, color.B);
#endregion
#region Manipulation
/// <summary>
/// Adds the given Hcl values to this color.
/// </summary>
/// <param name="h">The H value to add.</param>
/// <param name="c">The c value to add.</param>
/// <param name="l">The l value to add.</param>
/// <returns>The new color after the modification.</returns>
public static Color AddHcl(this in Color color, float h = 0, float c = 0, float l = 0)
{
(float cH, float cC, float cL) = color.GetHcl();
return Create(color.A, cH + h, cC + c, cL + l);
}
/// <summary>
/// Subtracts the given Hcl values to this color.
/// </summary>
/// <param name="h">The H value to subtract.</param>
/// <param name="c">The c value to subtract.</param>
/// <param name="l">The l value to subtract.</param>
/// <returns>The new color after the modification.</returns>
public static Color SubtractHcl(this in Color color, float h = 0, float c = 0, float l = 0)
{
(float cH, float cC, float cL) = color.GetHcl();
return Create(color.A, cH - h, cC - c, cL - l);
}
/// <summary>
/// Multiplies the given Hcl values to this color.
/// </summary>
/// <param name="h">The H value to multiply.</param>
/// <param name="c">The c value to multiply.</param>
/// <param name="l">The l value to multiply.</param>
/// <returns>The new color after the modification.</returns>
public static Color MultiplyHcl(this in Color color, float h = 1, float c = 1, float l = 1)
{
(float cH, float cC, float cL) = color.GetHcl();
return Create(color.A, cH * h, cC * c, cL * l);
}
/// <summary>
/// Divides the given Hcl values to this color.
/// </summary>
/// <param name="h">The H value to divide.</param>
/// <param name="c">The c value to divide.</param>
/// <param name="l">The l value to divide.</param>
/// <returns>The new color after the modification.</returns>
public static Color DivideHcl(this in Color color, float h = 1, float c = 1, float l = 1)
{
(float cH, float cC, float cL) = color.GetHcl();
return Create(color.A, cH / h, cC / c, cL / l);
}
/// <summary>
/// Sets the given X value of this color.
/// </summary>
/// <param name="h">The H value to set.</param>
/// <param name="c">The c value to set.</param>
/// <param name="l">The l value to set.</param>
/// <returns>The new color after the modification.</returns>
public static Color SetHcl(this in Color color, float? h = null, float? c = null, float? l = null)
{
(float cH, float cC, float cL) = color.GetHcl();
return Create(color.A, h ?? cH, c ?? cC, l ?? cL);
}
#endregion
#region Factory
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using Hcl-Values.
/// </summary>
/// <param name="h">The H component value of this <see cref="Color"/>.</param>
/// <param name="c">The c component value of this <see cref="Color"/>.</param>
/// <param name="l">The l component value of this <see cref="Color"/>.</param>
/// <returns>The color created from the values.</returns>
public static Color Create(float h, float c, float l)
=> Create(1.0f, h, c, l);
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using alpha and Hcl-Values.
/// </summary>
/// <param name="alpha">The alphc component value of this <see cref="Color"/>.</param>
/// <param name="h">The H component value of this <see cref="Color"/>.</param>
/// <param name="c">The c component value of this <see cref="Color"/>.</param>
/// <param name="l">The l component value of this <see cref="Color"/>.</param>
/// <returns>The color created from the values.</returns>
public static Color Create(byte alpha, float h, float c, float l)
=> Create((float)alpha / byte.MaxValue, h, c, l);
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using alpha and Hcl-Values.
/// </summary>
/// <param name="alpha">The alphc component value of this <see cref="Color"/>.</param>
/// <param name="h">The H component value of this <see cref="Color"/>.</param>
/// <param name="c">The c component value of this <see cref="Color"/>.</param>
/// <param name="l">The l component value of this <see cref="Color"/>.</param>
/// <returns>The color created from the values.</returns>
public static Color Create(int alpha, float h, float c, float l)
=> Create((float)alpha / byte.MaxValue, h, c, l);
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using alpha and Hcl-Values.
/// </summary>
/// <param name="alpha">The alphc component value of this <see cref="Color"/>.</param>
/// <param name="h">The H component value of this <see cref="Color"/>.</param>
/// <param name="c">The c component value of this <see cref="Color"/>.</param>
/// <param name="l">The l component value of this <see cref="Color"/>.</param>
/// <returns>The color created from the values.</returns>
public static Color Create(float alpha, float h, float c, float l)
{
(float r, float g, float _b) = CalculateRGBFromHcl(h, c, l);
return new Color(alpha, r, g, _b);
}
#endregion
#region Helper
private static (float h, float c, float l) CalculateHclFromRGB(float r, float g, float b)
{
const float RADIANS_DEGREES_CONVERSION = 180.0f / MathF.PI;
(float l, float a, float _b) = LabColor.CalculateLabFromRGB(r, g, b);
float h, c;
if (r.EqualsInTolerance(g) && r.EqualsInTolerance(b)) //DarthAffe 26.02.2021: The cumulated rounding errors are big enough to cause problems in that case
{
h = 0;
c = 0;
}
else
{
h = MathF.Atan2(_b, a);
if (h >= 0) h *= RADIANS_DEGREES_CONVERSION;
else h = 360 - (-h * RADIANS_DEGREES_CONVERSION);
c = MathF.Sqrt((a * a) + (_b * _b));
}
return (h, c, l);
}
private static (float r, float g, float b) CalculateRGBFromHcl(float h, float c, float l)
{
const float DEGREES_RADIANS_CONVERSION = MathF.PI / 180.0f;
h *= DEGREES_RADIANS_CONVERSION;
float a = c * MathF.Cos(h);
float b = c * MathF.Sin(h);
return LabColor.CalculateRGBFromLab(l, a, b);
}
#endregion
}
}

View File

@ -0,0 +1,219 @@
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable UnusedMember.Global
using System;
namespace RGB.NET.Core
{
public static class LabColor
{
#region Getter
/// <summary>
/// Gets the L component value (Lab-color space) of this <see cref="Color"/> in the range [0..100].
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
public static float GetLabL(this in Color color) => color.GetLab().l;
/// <summary>
/// Gets the a component value (Lab-color space) of this <see cref="Color"/> in the range [0..1].
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
public static float GetLabA(this in Color color) => color.GetLab().a;
/// <summary>
/// Gets the b component value (Lab-color space) of this <see cref="Color"/> in the range [0..1].
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
public static float GetLabB(this in Color color) => color.GetLab().b;
/// <summary>
/// Gets the L, a and b component values (Lab-color space) of this <see cref="Color"/>.
/// L in the range [0..100].
/// a in the range [0..1].
/// b in the range [0..1].
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
public static (float l, float a, float b) GetLab(this in Color color)
=> CalculateLabFromRGB(color.R, color.G, color.B);
#endregion
#region Manipulation
/// <summary>
/// Adds the given Lab values to this color.
/// </summary>
/// <param name="l">The L value to add.</param>
/// <param name="a">The a value to add.</param>
/// <param name="b">The b value to add.</param>
/// <returns>The new color after the modification.</returns>
public static Color AddLab(this in Color color, float l = 0, float a = 0, float b = 0)
{
(float cL, float cA, float cB) = color.GetLab();
return Create(color.A, cL + l, cA + a, cB + b);
}
/// <summary>
/// Subtracts the given Lab values to this color.
/// </summary>
/// <param name="l">The L value to subtract.</param>
/// <param name="a">The a value to subtract.</param>
/// <param name="b">The b value to subtract.</param>
/// <returns>The new color after the modification.</returns>
public static Color SubtractLab(this in Color color, float l = 0, float a = 0, float b = 0)
{
(float cL, float cA, float cB) = color.GetLab();
return Create(color.A, cL - l, cA - a, cB - b);
}
/// <summary>
/// Multiplies the given Lab values to this color.
/// </summary>
/// <param name="l">The L value to multiply.</param>
/// <param name="a">The a value to multiply.</param>
/// <param name="b">The b value to multiply.</param>
/// <returns>The new color after the modification.</returns>
public static Color MultiplyLab(this in Color color, float l = 1, float a = 1, float b = 1)
{
(float cL, float cA, float cB) = color.GetLab();
return Create(color.A, cL * l, cA * a, cB * b);
}
/// <summary>
/// Divides the given Lab values to this color.
/// </summary>
/// <param name="l">The L value to divide.</param>
/// <param name="a">The a value to divide.</param>
/// <param name="b">The b value to divide.</param>
/// <returns>The new color after the modification.</returns>
public static Color DivideLab(this in Color color, float l = 1, float a = 1, float b = 1)
{
(float cL, float cA, float cB) = color.GetLab();
return Create(color.A, cL / l, cA / a, cB / b);
}
/// <summary>
/// Sets the given X valueof this color.
/// </summary>
/// <param name="l">The L value to set.</param>
/// <param name="a">The a value to set.</param>
/// <param name="b">The b value to set.</param>
/// <returns>The new color after the modification.</returns>
public static Color SetLab(this in Color color, float? l = null, float? a = null, float? b = null)
{
(float cL, float cA, float cB) = color.GetLab();
return Create(color.A, l ?? cL, a ?? cA, b ?? cB);
}
#endregion
#region Factory
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using Lab-Values.
/// </summary>
/// <param name="l">The L component value of this <see cref="Color"/>.</param>
/// <param name="a">The a component value of this <see cref="Color"/>.</param>
/// <param name="b">The b component value of this <see cref="Color"/>.</param>
/// <returns>The color created from the values.</returns>
public static Color Create(float l, float a, float b)
=> Create(1.0f, l, a, b);
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using alpha and Lab-Values.
/// </summary>
/// <param name="alpha">The alpha component value of this <see cref="Color"/>.</param>
/// <param name="l">The L component value of this <see cref="Color"/>.</param>
/// <param name="a">The a component value of this <see cref="Color"/>.</param>
/// <param name="b">The b component value of this <see cref="Color"/>.</param>
/// <returns>The color created from the values.</returns>
public static Color Create(byte alpha, float l, float a, float b)
=> Create((float)alpha / byte.MaxValue, l, a, b);
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using alpha and Lab-Values.
/// </summary>
/// <param name="alpha">The alpha component value of this <see cref="Color"/>.</param>
/// <param name="l">The L component value of this <see cref="Color"/>.</param>
/// <param name="a">The a component value of this <see cref="Color"/>.</param>
/// <param name="b">The b component value of this <see cref="Color"/>.</param>
/// <returns>The color created from the values.</returns>
public static Color Create(int alpha, float l, float a, float b)
=> Create((float)alpha / byte.MaxValue, l, a, b);
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using alpha and Lab-Values.
/// </summary>
/// <param name="alpha">The alpha component value of this <see cref="Color"/>.</param>
/// <param name="l">The L component value of this <see cref="Color"/>.</param>
/// <param name="a">The a component value of this <see cref="Color"/>.</param>
/// <param name="b">The b component value of this <see cref="Color"/>.</param>
/// <returns>The color created from the values.</returns>
public static Color Create(float alpha, float l, float a, float b)
{
(float r, float g, float _b) = CalculateRGBFromLab(l, a, b);
return new Color(alpha, r, g, _b);
}
#endregion
#region Helper
internal static (float l, float a, float b) CalculateLabFromRGB(float r, float g, float b)
{
(float x, float y, float z) = XYZColor.CaclulateXYZFromRGB(r, g, b);
return CaclulateLabFromXYZ(x, y, z);
}
internal static (float r, float g, float b) CalculateRGBFromLab(float l, float a, float b)
{
(float x, float y, float z) = CalculateXYZFromLab(l, a, b);
return XYZColor.CalculateRGBFromXYZ(x, y, z);
}
private static (float l, float a, float b) CaclulateLabFromXYZ(float x, float y, float z)
{
const float ONETHRID = 1.0f / 3.0f;
const float FACTOR2 = 16.0f / 116.0f;
x /= 95.047f;
y /= 100.0f;
z /= 108.883f;
x = ((x > 0.008856f) ? (MathF.Pow(x, ONETHRID)) : ((7.787f * x) + FACTOR2));
y = ((y > 0.008856f) ? (MathF.Pow(y, ONETHRID)) : ((7.787f * y) + FACTOR2));
z = ((z > 0.008856f) ? (MathF.Pow(z, ONETHRID)) : ((7.787f * z) + FACTOR2));
float l = (116.0f * y) - 16.0f;
float a = 500.0f * (x - y);
float b = 200.0f * (y - z);
return (l, a, b);
}
private static (float x, float y, float z) CalculateXYZFromLab(float l, float a, float b)
{
const float FACTOR2 = 16.0f / 116.0f;
float y = (l + 16.0f) / 116.0f;
float x = (a / 500.0f) + y;
float z = y - (b / 200.0f);
float powX = MathF.Pow(x, 3.0f);
float powY = MathF.Pow(y, 3.0f);
float powZ = MathF.Pow(z, 3.0f);
x = ((powX > 0.008856f) ? (powX) : ((x - FACTOR2) / 7.787f));
y = ((powY > 0.008856f) ? (powY) : ((y - FACTOR2) / 7.787f));
z = ((powZ > 0.008856f) ? (powZ) : ((z - FACTOR2) / 7.787f));
return (x * 95.047f, y * 100.0f, z * 108.883f);
}
#endregion
}
}

View File

@ -13,35 +13,35 @@ namespace RGB.NET.Core
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
public static byte GetA(this Color color) => color.A.GetByteValueFromPercentage();
public static byte GetA(this in Color color) => color.A.GetByteValueFromPercentage();
/// <summary>
/// Gets the R component value of this <see cref="Color"/> as byte in the range [0..255].
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
public static byte GetR(this Color color) => color.R.GetByteValueFromPercentage();
public static byte GetR(this in Color color) => color.R.GetByteValueFromPercentage();
/// <summary>
/// Gets the G component value of this <see cref="Color"/> as byte in the range [0..255].
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
public static byte GetG(this Color color) => color.G.GetByteValueFromPercentage();
public static byte GetG(this in Color color) => color.G.GetByteValueFromPercentage();
/// <summary>
/// Gets the B component value of this <see cref="Color"/> as byte in the range [0..255].
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
public static byte GetB(this Color color) => color.B.GetByteValueFromPercentage();
public static byte GetB(this in Color color) => color.B.GetByteValueFromPercentage();
/// <summary>
/// Gets the A, R, G and B component value of this <see cref="Color"/> as byte in the range [0..255].
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
public static (byte a, byte r, byte g, byte b) GetRGBBytes(this Color color)
public static (byte a, byte r, byte g, byte b) GetRGBBytes(this in Color color)
=> (color.GetA(), color.GetR(), color.GetG(), color.GetB());
/// <summary>
@ -49,7 +49,7 @@ namespace RGB.NET.Core
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
public static (double a, double r, double g, double b) GetRGB(this Color color)
public static (float a, float r, float g, float b) GetRGB(this in Color color)
=> (color.A, color.R, color.G, color.B);
#endregion
@ -65,7 +65,7 @@ namespace RGB.NET.Core
/// <param name="g">The green value to add.</param>
/// <param name="b">The blue value to add.</param>
/// <returns>The new color after the modification.</returns>
public static Color AddRGB(this Color color, int r = 0, int g = 0, int b = 0)
public static Color AddRGB(this in Color color, int r = 0, int g = 0, int b = 0)
=> new(color.A, color.GetR() + r, color.GetG() + g, color.GetB() + b);
/// <summary>
@ -75,7 +75,7 @@ namespace RGB.NET.Core
/// <param name="g">The green value to add.</param>
/// <param name="b">The blue value to add.</param>
/// <returns>The new color after the modification.</returns>
public static Color AddRGB(this Color color, double r = 0, double g = 0, double b = 0)
public static Color AddRGB(this in Color color, float r = 0, float g = 0, float b = 0)
=> new(color.A, color.R + r, color.G + g, color.B + b);
/// <summary>
@ -83,7 +83,7 @@ namespace RGB.NET.Core
/// </summary>
/// <param name="a">The alpha value to add.</param>
/// <returns>The new color after the modification.</returns>
public static Color AddA(this Color color, int a)
public static Color AddA(this in Color color, int a)
=> new(color.GetA() + a, color.R, color.G, color.B);
/// <summary>
@ -91,7 +91,7 @@ namespace RGB.NET.Core
/// </summary>
/// <param name="a">The alpha value to add.</param>
/// <returns>The new color after the modification.</returns>
public static Color AddA(this Color color, double a)
public static Color AddA(this in Color color, float a)
=> new(color.A + a, color.R, color.G, color.B);
#endregion
@ -105,7 +105,7 @@ namespace RGB.NET.Core
/// <param name="g">The green value to subtract.</param>
/// <param name="b">The blue value to subtract.</param>
/// <returns>The new color after the modification.</returns>
public static Color SubtractRGB(this Color color, int r = 0, int g = 0, int b = 0)
public static Color SubtractRGB(this in Color color, int r = 0, int g = 0, int b = 0)
=> new(color.A, color.GetR() - r, color.GetG() - g, color.GetB() - b);
/// <summary>
@ -115,7 +115,7 @@ namespace RGB.NET.Core
/// <param name="g">The green value to subtract.</param>
/// <param name="b">The blue value to subtract.</param>
/// <returns>The new color after the modification.</returns>
public static Color SubtractRGB(this Color color, double r = 0, double g = 0, double b = 0)
public static Color SubtractRGB(this in Color color, float r = 0, float g = 0, float b = 0)
=> new(color.A, color.R - r, color.G - g, color.B - b);
/// <summary>
@ -123,7 +123,7 @@ namespace RGB.NET.Core
/// </summary>
/// <param name="a">The alpha value to subtract.</param>
/// <returns>The new color after the modification.</returns>
public static Color SubtractA(this Color color, int a)
public static Color SubtractA(this in Color color, int a)
=> new(color.GetA() - a, color.R, color.G, color.B);
/// <summary>
@ -131,7 +131,7 @@ namespace RGB.NET.Core
/// </summary>
/// <param name="a">The alpha value to subtract.</param>
/// <returns>The new color after the modification.</returns>
public static Color SubtractA(this Color color, double aPercent)
public static Color SubtractA(this in Color color, float aPercent)
=> new(color.A - aPercent, color.R, color.G, color.B);
#endregion
@ -145,7 +145,7 @@ namespace RGB.NET.Core
/// <param name="g">The green value to multiply.</param>
/// <param name="b">The blue value to multiply.</param>
/// <returns>The new color after the modification.</returns>
public static Color MultiplyRGB(this Color color, double r = 1, double g = 1, double b = 1)
public static Color MultiplyRGB(this in Color color, float r = 1, float g = 1, float b = 1)
=> new(color.A, color.R * r, color.G * g, color.B * b);
/// <summary>
@ -153,7 +153,7 @@ namespace RGB.NET.Core
/// </summary>
/// <param name="a">The alpha value to multiply.</param>
/// <returns>The new color after the modification.</returns>
public static Color MultiplyA(this Color color, double a)
public static Color MultiplyA(this in Color color, float a)
=> new(color.A * a, color.R, color.G, color.B);
#endregion
@ -167,7 +167,7 @@ namespace RGB.NET.Core
/// <param name="g">The green value to divide.</param>
/// <param name="b">The blue value to divide.</param>
/// <returns>The new color after the modification.</returns>
public static Color DivideRGB(this Color color, double r = 1, double g = 1, double b = 1)
public static Color DivideRGB(this in Color color, float r = 1, float g = 1, float b = 1)
=> new(color.A, color.R / r, color.G / g, color.B / b);
/// <summary>
@ -175,7 +175,7 @@ namespace RGB.NET.Core
/// </summary>
/// <param name="a">The alpha value to divide.</param>
/// <returns>The new color after the modification.</returns>
public static Color DivideA(this Color color, double a)
public static Color DivideA(this in Color color, float a)
=> new(color.A / a, color.R, color.G, color.B);
#endregion
@ -189,7 +189,7 @@ namespace RGB.NET.Core
/// <param name="g">The green value to set.</param>
/// <param name="b">The blue value to set.</param>
/// <returns>The new color after the modification.</returns>
public static Color SetRGB(this Color color, byte? r = null, byte? g = null, byte? b = null)
public static Color SetRGB(this in Color color, byte? r = null, byte? g = null, byte? b = null)
=> new(color.A, r ?? color.GetR(), g ?? color.GetG(), b ?? color.GetB());
/// <summary>
@ -199,7 +199,7 @@ namespace RGB.NET.Core
/// <param name="g">The green value to set.</param>
/// <param name="b">The blue value to set.</param>
/// <returns>The new color after the modification.</returns>
public static Color SetRGB(this Color color, int? r = null, int? g = null, int? b = null)
public static Color SetRGB(this in Color color, int? r = null, int? g = null, int? b = null)
=> new(color.A, r ?? color.GetR(), g ?? color.GetG(), b ?? color.GetB());
/// <summary>
@ -209,7 +209,7 @@ namespace RGB.NET.Core
/// <param name="g">The green value to set.</param>
/// <param name="b">The blue value to set.</param>
/// <returns>The new color after the modification.</returns>
public static Color SetRGB(this Color color, double? r = null, double? g = null, double? b = null)
public static Color SetRGB(this in Color color, float? r = null, float? g = null, float? b = null)
=> new(color.A, r ?? color.R, g ?? color.G, b ?? color.B);
/// <summary>
@ -217,14 +217,14 @@ namespace RGB.NET.Core
/// </summary>
/// <param name="a">The alpha value to set.</param>
/// <returns>The new color after the modification.</returns>
public static Color SetA(this Color color, int a) => new(a, color.R, color.G, color.B);
public static Color SetA(this in Color color, int a) => new(a, color.R, color.G, color.B);
/// <summary>
/// Sets the given alpha value of this color.
/// </summary>
/// <param name="a">The alpha value to set.</param>
/// <returns>The new color after the modification.</returns>
public static Color SetA(this Color color, double a) => new(a, color.R, color.G, color.B);
public static Color SetA(this in Color color, float a) => new(a, color.R, color.G, color.B);
#endregion
@ -236,13 +236,13 @@ namespace RGB.NET.Core
/// Gets the current color as a RGB-HEX-string.
/// </summary>
/// <returns>The RGB-HEX-string.</returns>
public static string AsRGBHexString(this Color color, bool leadingHash = true) => (leadingHash ? "#" : "") + ConversionHelper.ToHex(color.GetR(), color.GetG(), color.GetB());
public static string AsRGBHexString(this in Color color, bool leadingHash = true) => (leadingHash ? "#" : "") + ConversionHelper.ToHex(color.GetR(), color.GetG(), color.GetB());
/// <summary>
/// Gets the current color as a ARGB-HEX-string.
/// </summary>
/// <returns>The ARGB-HEX-string.</returns>
public static string AsARGBHexString(this Color color, bool leadingHash = true) => (leadingHash ? "#" : "") + ConversionHelper.ToHex(color.GetA(), color.GetR(), color.GetG(), color.GetB());
public static string AsARGBHexString(this in Color color, bool leadingHash = true) => (leadingHash ? "#" : "") + ConversionHelper.ToHex(color.GetA(), color.GetR(), color.GetG(), color.GetB());
#endregion

View File

@ -0,0 +1,200 @@
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable UnusedMember.Global
using System;
namespace RGB.NET.Core
{
public static class XYZColor
{
#region Getter
/// <summary>
/// Gets the X component value (XYZ-color space) of this <see cref="Color"/> in the range [0..95.047].
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
public static float GetX(this in Color color) => color.GetXYZ().x;
/// <summary>
/// Gets the Y component value (XYZ-color space) of this <see cref="Color"/> in the range [0..100].
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
public static float GetY(this in Color color) => color.GetXYZ().y;
/// <summary>
/// Gets the Z component value (XYZ-color space) of this <see cref="Color"/> in the range [0..108.883].
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
public static float GetZ(this in Color color) => color.GetXYZ().z;
/// <summary>
/// Gets the X, Y and Z component values (XYZ-color space) of this <see cref="Color"/>.
/// X in the range [0..95.047].
/// Y in the range [0..100].
/// Z in the range [0..108.883].
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
public static (float x, float y, float z) GetXYZ(this in Color color)
=> CaclulateXYZFromRGB(color.R, color.G, color.B);
#endregion
#region Manipulation
/// <summary>
/// Adds the given XYZ values to this color.
/// </summary>
/// <param name="x">The X value to add.</param>
/// <param name="y">The Y value to add.</param>
/// <param name="z">The Z value to add.</param>
/// <returns>The new color after the modification.</returns>
public static Color AddXYZ(this in Color color, float x = 0, float y = 0, float z = 0)
{
(float cX, float cY, float cZ) = color.GetXYZ();
return Create(color.A, cX + x, cY + y, cZ + z);
}
/// <summary>
/// Subtracts the given XYZ values to this color.
/// </summary>
/// <param name="x">The X value to subtract.</param>
/// <param name="y">The Y value to subtract.</param>
/// <param name="z">The Z value to subtract.</param>
/// <returns>The new color after the modification.</returns>
public static Color SubtractXYZ(this in Color color, float x = 0, float y = 0, float z = 0)
{
(float cX, float cY, float cZ) = color.GetXYZ();
return Create(color.A, cX - x, cY - y, cZ - z);
}
/// <summary>
/// Multiplies the given XYZ values to this color.
/// </summary>
/// <param name="x">The X value to multiply.</param>
/// <param name="y">The Y value to multiply.</param>
/// <param name="z">The Z value to multiply.</param>
/// <returns>The new color after the modification.</returns>
public static Color MultiplyXYZ(this in Color color, float x = 1, float y = 1, float z = 1)
{
(float cX, float cY, float cZ) = color.GetXYZ();
return Create(color.A, cX * x, cY * y, cZ * z);
}
/// <summary>
/// Divides the given XYZ values to this color.
/// </summary>
/// <param name="x">The X value to divide.</param>
/// <param name="y">The Y value to divide.</param>
/// <param name="z">The Z value to divide.</param>
/// <returns>The new color after the modification.</returns>
public static Color DivideXYZ(this in Color color, float x = 1, float y = 1, float z = 1)
{
(float cX, float cY, float cZ) = color.GetXYZ();
return Create(color.A, cX / x, cY / y, cZ / z);
}
/// <summary>
/// Sets the given X valueof this color.
/// </summary>
/// <param name="x">The X value to set.</param>
/// <param name="y">The Y value to set.</param>
/// <param name="z">The Z value to set.</param>
/// <returns>The new color after the modification.</returns>
public static Color SetXYZ(this in Color color, float? x = null, float? y = null, float? value = null)
{
(float cX, float cY, float cZ) = color.GetXYZ();
return Create(color.A, x ?? cX, y ?? cY, value ?? cZ);
}
#endregion
#region Factory
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using XYZ-Values.
/// </summary>
/// <param name="x">The X component value of this <see cref="Color"/>.</param>
/// <param name="y">The Y component value of this <see cref="Color"/>.</param>
/// <param name="z">The Z component value of this <see cref="Color"/>.</param>
/// <returns>The color created from the values.</returns>
public static Color Create(float x, float y, float z)
=> Create(1.0f, x, y, z);
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using alpha and XYZ-Values.
/// </summary>
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
/// <param name="x">The X component value of this <see cref="Color"/>.</param>
/// <param name="y">The Y component value of this <see cref="Color"/>.</param>
/// <param name="z">The Z component value of this <see cref="Color"/>.</param>
/// <returns>The color created from the values.</returns>
public static Color Create(byte a, float x, float y, float z)
=> Create((float)a / byte.MaxValue, x, y, z);
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using alpha and XYZ-Values.
/// </summary>
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
/// <param name="x">The X component value of this <see cref="Color"/>.</param>
/// <param name="y">The Y component value of this <see cref="Color"/>.</param>
/// <param name="z">The Z component value of this <see cref="Color"/>.</param>
/// <returns>The color created from the values.</returns>
public static Color Create(int a, float x, float y, float z)
=> Create((float)a / byte.MaxValue, x, y, z);
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using alpha and XYZ-Values.
/// </summary>
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
/// <param name="x">The X component value of this <see cref="Color"/>.</param>
/// <param name="y">The Y component value of this <see cref="Color"/>.</param>
/// <param name="z">The Z component value of this <see cref="Color"/>.</param>
/// <returns>The color created from the values.</returns>
public static Color Create(float a, float x, float y, float z)
{
(float r, float g, float b) = CalculateRGBFromXYZ(x, y, z);
return new Color(a, r, g, b);
}
#endregion
#region Helper
internal static (float x, float y, float z) CaclulateXYZFromRGB(float r, float g, float b)
{
r = ((r > 0.04045f) ? MathF.Pow(((r + 0.055f) / 1.055f), 2.4f) : (r / 12.92f)) * 100.0f;
g = ((g > 0.04045f) ? MathF.Pow(((g + 0.055f) / 1.055f), 2.4f) : (g / 12.92f)) * 100.0f;
b = ((b > 0.04045f) ? MathF.Pow(((b + 0.055f) / 1.055f), 2.4f) : (b / 12.92f)) * 100.0f;
float x = (r * 0.4124f) + (g * 0.3576f) + (b * 0.1805f);
float y = (r * 0.2126f) + (g * 0.7152f) + (b * 0.0722f);
float z = (r * 0.0193f) + (g * 0.1192f) + (b * 0.9505f);
return (x, y, z);
}
internal static (float r, float g, float b) CalculateRGBFromXYZ(float x, float y, float z)
{
const float INVERSE_EXPONENT = 1.0f / 2.4f;
x /= 100.0f;
y /= 100.0f;
z /= 100.0f;
float r = (x * 3.2406f) + (y * -1.5372f) + (z * -0.4986f);
float g = (x * -0.9689f) + (y * 1.8758f) + (z * 0.0415f);
float b = (x * 0.0557f) + (y * -0.2040f) + (z * 1.0570f);
r = ((r > 0.0031308f) ? ((1.055f * (MathF.Pow(r, INVERSE_EXPONENT))) - 0.055f) : (12.92f * r));
g = ((g > 0.0031308f) ? ((1.055f * (MathF.Pow(g, INVERSE_EXPONENT))) - 0.055f) : (12.92f * g));
b = ((b > 0.0031308f) ? ((1.055f * (MathF.Pow(b, INVERSE_EXPONENT))) - 0.055f) : (12.92f * b));
return (r, g, b);
}
#endregion
}
}

View File

@ -11,6 +11,6 @@ namespace RGB.NET.Core
/// Applies the <see cref="IColorCorrection"/> to the given <see cref="Color"/>.
/// </summary>
/// <param name="color">The <see cref="Color"/> to correct.</param>
Color ApplyTo(Color color);
void ApplyTo(ref Color color);
}
}

View File

@ -12,6 +12,6 @@
/// <param name="rectangle">The rectangle in which the <see cref="IBrush"/> should be drawn.</param>
/// <param name="renderTarget">The target (key/point) from which the <see cref="Color"/> should be taken.</param>
/// <param name="color">The <see cref="Color"/> to be modified.</param>
Color ManipulateColor(Rectangle rectangle, BrushRenderTarget renderTarget, Color color);
void ManipulateColor(in Rectangle rectangle, in RenderTarget renderTarget, ref Color color);
}
}

View File

@ -34,11 +34,14 @@ namespace RGB.NET.Core
}
/// <inheritdoc />
public abstract TDeviceInfo DeviceInfo { get; }
public TDeviceInfo DeviceInfo { get; }
/// <inheritdoc />
IRGBDeviceInfo IRGBDevice.DeviceInfo => DeviceInfo;
/// <inheritdoc />
public IList<IColorCorrection> ColorCorrections { get; } = new List<IColorCorrection>();
/// <summary>
/// Gets or sets if the device needs to be flushed on every update.
/// </summary>
@ -49,6 +52,8 @@ namespace RGB.NET.Core
/// </summary>
protected Dictionary<LedId, Led> LedMapping { get; } = new();
protected IUpdateQueue UpdateQueue { get; }
#region Indexer
/// <inheritdoc />
@ -65,6 +70,16 @@ namespace RGB.NET.Core
#endregion
#region Constructors
protected AbstractRGBDevice(TDeviceInfo deviceOnfo, IUpdateQueue updateQueue)
{
this.DeviceInfo = deviceOnfo;
this.UpdateQueue = updateQueue;
}
#endregion
#region Methods
/// <inheritdoc />
@ -75,22 +90,51 @@ namespace RGB.NET.Core
// Send LEDs to SDK
List<Led> ledsToUpdate = GetLedsToUpdate(flushLeds).ToList();
foreach (Led ledToUpdate in ledsToUpdate)
ledToUpdate.Update();
foreach (Led led in ledsToUpdate)
led.Update();
UpdateLeds(ledsToUpdate);
}
protected virtual IEnumerable<Led> GetLedsToUpdate(bool flushLeds) => ((RequiresFlush || flushLeds) ? LedMapping.Values : LedMapping.Values.Where(x => x.IsDirty));
protected virtual IEnumerable<Led> GetLedsToUpdate(bool flushLeds) => ((RequiresFlush || flushLeds) ? LedMapping.Values : LedMapping.Values.Where(x => x.IsDirty)).Where(led => led.RequestedColor?.A > 0);
protected virtual IEnumerable<(object key, Color color)> GetUpdateData(IEnumerable<Led> leds)
{
if (ColorCorrections.Count > 0)
{
foreach (Led led in leds)
{
Color color = led.Color;
object key = led.CustomData ?? led.Id;
foreach (IColorCorrection colorCorrection in ColorCorrections)
colorCorrection.ApplyTo(ref color);
yield return (key, color);
}
}
else
{
foreach (Led led in leds)
{
Color color = led.Color;
object key = led.CustomData ?? led.Id;
yield return (key, color);
}
}
}
/// <summary>
/// Sends all the updated <see cref="Led"/> to the device.
/// </summary>
protected virtual void UpdateLeds(IEnumerable<Led> ledsToUpdate) => UpdateQueue.SetData(GetUpdateData(ledsToUpdate));
/// <inheritdoc />
public virtual void Dispose()
{
try
{
LedMapping.Clear();
}
catch { /* this really shouldn't happen */ }
try { UpdateQueue.Dispose(); } catch { /* :( */ }
try { LedMapping.Clear(); } catch { /* this really shouldn't happen */ }
}
/// <summary>
@ -99,11 +143,6 @@ namespace RGB.NET.Core
protected virtual void DeviceUpdate()
{ }
/// <summary>
/// Sends all the updated <see cref="Led"/> to the device.
/// </summary>
protected abstract void UpdateLeds(IEnumerable<Led> ledsToUpdate);
/// <summary>
/// Initializes the <see cref="Led"/> with the specified id.
/// </summary>
@ -111,7 +150,7 @@ namespace RGB.NET.Core
/// <param name="location">The location of the <see cref="Led"/> to initialize.</param>
/// <param name="size">The size of the <see cref="Led"/> to initialize.</param>
/// <returns>The initialized led.</returns>
public virtual Led? AddLed(LedId ledId, Point location, Size size, object? customData = null)
public virtual Led? AddLed(LedId ledId, in Point location, in Size size, object? customData = null)
{
if ((ledId == LedId.Invalid) || LedMapping.ContainsKey(ledId)) return null;

View File

@ -0,0 +1,121 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
namespace RGB.NET.Core
{
public abstract class AbstractRGBDeviceProvider : IRGBDeviceProvider
{
#region Properties & Fields
private readonly double _defaultUpdateRateHardLimit;
public bool IsInitialized { get; protected set; }
public bool ThrowsExceptions { get; protected set; }
public virtual IEnumerable<IRGBDevice> Devices { get; protected set; } = Enumerable.Empty<IRGBDevice>();
protected Dictionary<int, IDeviceUpdateTrigger> UpdateTriggers { get; } = new();
#endregion
#region Events
public event EventHandler<Exception>? Exception;
#endregion
#region Constructors
protected AbstractRGBDeviceProvider(double defaultUpdateRateHardLimit = 0)
{
this._defaultUpdateRateHardLimit = defaultUpdateRateHardLimit;
}
#endregion
#region Methods
public bool Initialize(RGBDeviceType loadFilter = RGBDeviceType.All, bool throwExceptions = false)
{
ThrowsExceptions = throwExceptions;
try
{
Reset();
InitializeSDK();
Devices = new ReadOnlyCollection<IRGBDevice>(GetLoadedDevices(loadFilter).ToList());
foreach (IDeviceUpdateTrigger updateTrigger in UpdateTriggers.Values)
updateTrigger.Start();
IsInitialized = true;
}
catch (Exception ex)
{
Reset();
Throw(ex);
return false;
}
return true;
}
protected virtual IEnumerable<IRGBDevice> GetLoadedDevices(RGBDeviceType loadFilter)
{
foreach (IRGBDevice device in LoadDevices())
{
if (loadFilter.HasFlag(device.DeviceInfo.DeviceType))
yield return device;
else
device.Dispose();
}
}
protected abstract void InitializeSDK();
protected abstract IEnumerable<IRGBDevice> LoadDevices();
protected virtual IDeviceUpdateTrigger GetUpdateTrigger(int id = -1, double? updateRateHardLimit = null)
{
if (!UpdateTriggers.TryGetValue(id, out IDeviceUpdateTrigger? updaeTrigger))
UpdateTriggers[id] = (updaeTrigger = CreateUpdateTrigger(id, updateRateHardLimit ?? _defaultUpdateRateHardLimit));
return updaeTrigger;
}
protected virtual IDeviceUpdateTrigger CreateUpdateTrigger(int id, double updateRateHardLimit) => new DeviceUpdateTrigger(updateRateHardLimit);
protected virtual void Reset()
{
foreach (IDeviceUpdateTrigger updateTrigger in UpdateTriggers.Values)
updateTrigger.Dispose();
Devices = Enumerable.Empty<IRGBDevice>();
IsInitialized = false;
}
protected virtual void Throw(Exception ex)
{
try { OnException(ex); } catch { /* we don't want to throw due to bad event handlers */ }
if (ThrowsExceptions)
throw ex;
}
protected virtual void OnException(Exception ex) => Exception?.Invoke(this, ex);
public virtual void Dispose()
{
IEnumerable<IRGBDevice> devices = Devices;
Reset();
foreach (IRGBDevice device in devices)
device.Dispose();
}
#endregion
}
}

View File

@ -19,7 +19,9 @@ namespace RGB.NET.Core
/// Gets generic information about the <see cref="IRGBDevice"/>.
/// </summary>
IRGBDeviceInfo DeviceInfo { get; }
IList<IColorCorrection> ColorCorrections { get; }
#endregion
#region Indexer
@ -56,7 +58,7 @@ namespace RGB.NET.Core
/// <param name="flushLeds">Specifies whether all <see cref="Led"/> (including clean ones) should be updated.</param>
void Update(bool flushLeds = false);
Led? AddLed(LedId ledId, Point location, Size size, object? customData = null);
Led? AddLed(LedId ledId, in Point location, in Size size, object? customData = null);
Led? RemoveLed(LedId ledId);

View File

@ -22,6 +22,15 @@ namespace RGB.NET.Core
#endregion
#region Events
/// <summary>
/// Occurs when an exception is thrown in the device provider
/// </summary>
event EventHandler<Exception>? Exception;
#endregion
#region Methods
bool Initialize(RGBDeviceType loadFilter = RGBDeviceType.All, bool throwExceptions = false);

View File

@ -2,7 +2,6 @@
// ReSharper disable UnusedAutoPropertyAccessor.Global
using System;
using System.Collections.Generic;
namespace RGB.NET.Core
{

View File

@ -13,7 +13,7 @@ namespace RGB.NET.Core
/// <param name="color1">The start color of the distance calculation.</param>
/// <param name="color2">The end color fot the distance calculation.</param>
/// <returns></returns>
public static double DistanceTo(this Color color1, Color color2)
public static double DistanceTo(this in Color color1, in Color color2)
{
(_, byte r1, byte g1, byte b1) = color1.GetRGBBytes();
(_, byte r2, byte g2, byte b2) = color2.GetRGBBytes();

View File

@ -4,16 +4,16 @@ using System.Runtime.CompilerServices;
namespace RGB.NET.Core
{
/// <summary>
/// Offers some extensions and helper-methods for the work with doubles
/// Offers some extensions and helper-methods for the work with floats
/// </summary>
public static class DoubleExtensions
public static class FloatExtensions
{
#region Constants
/// <summary>
/// Defines the precision RGB.NET processes floating point comparisons in.
/// </summary>
public const double TOLERANCE = 1E-10;
public const float TOLERANCE = 1E-7f;
#endregion
@ -26,7 +26,7 @@ namespace RGB.NET.Core
/// <param name="value2">The first value to compare.</param>
/// <returns><c>true</c> if the difference is smaller than the <see cref="TOLERANCE"/>; otherwise, <c>false</c>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool EqualsInTolerance(this double value1, double value2) => Math.Abs(value1 - value2) < TOLERANCE;
public static bool EqualsInTolerance(this float value1, float value2) => Math.Abs(value1 - value2) < TOLERANCE;
/// <summary>
/// Clamps the provided value to be bigger or equal min and smaller or equal max.
@ -36,7 +36,7 @@ namespace RGB.NET.Core
/// <param name="max">The higher value of the range the value is clamped to.</param>
/// <returns>The clamped value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static double Clamp(this double value, double min, double max)
public static float Clamp(this float value, float min, float max)
{
// ReSharper disable ConvertIfStatementToReturnStatement - I'm not sure why, but inlining this statement reduces performance by ~10%
if (value < min) return min;
@ -70,9 +70,9 @@ namespace RGB.NET.Core
/// <param name="max">The higher value of the range the value is wrapped into.</param>
/// <returns>The wrapped value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static double Wrap(this double value, double min, double max)
public static float Wrap(this float value, float min, float max)
{
double range = max - min;
float range = max - min;
while (value >= max)
value -= range;
@ -84,17 +84,17 @@ namespace RGB.NET.Core
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static byte GetByteValueFromPercentage(this double percentage)
public static byte GetByteValueFromPercentage(this float percentage)
{
if (double.IsNaN(percentage)) return 0;
if (float.IsNaN(percentage)) return 0;
percentage = percentage.Clamp(0, 1.0);
percentage = percentage.Clamp(0, 1.0f);
return (byte)(percentage >= 1.0 ? 255 : percentage * 256.0);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static double GetPercentageFromByteValue(this byte value)
=> ((double)value) / byte.MaxValue;
public static float GetPercentageFromByteValue(this byte value)
=> ((float)value) / byte.MaxValue;
#endregion
}

View File

@ -13,7 +13,7 @@ namespace RGB.NET.Core
/// <param name="x">The x-ammount to move.</param>
/// <param name="y">The y-ammount to move.</param>
/// <returns>The new location of the point.</returns>
public static Point Translate(this Point point, double x = 0, double y = 0) => new(point.X + x, point.Y + y);
public static Point Translate(this in Point point, float x = 0, float y = 0) => new(point.X + x, point.Y + y);
/// <summary>
/// Rotates the specified <see cref="Point"/> by the given amuont around the given origin.
@ -22,14 +22,18 @@ namespace RGB.NET.Core
/// <param name="rotation">The rotation.</param>
/// <param name="origin">The origin to rotate around. [0,0] if not set.</param>
/// <returns>The new location of the point.</returns>
public static Point Rotate(this Point point, Rotation rotation, Point origin = new())
public static Point Rotate(this in Point point, in Rotation rotation, in Point origin = new())
{
double sin = Math.Sin(rotation.Radians);
double cos = Math.Cos(rotation.Radians);
float sin = MathF.Sin(rotation.Radians);
float cos = MathF.Cos(rotation.Radians);
point = new Point(point.X - origin.X, point.Y - origin.Y);
point = new Point((point.X * cos) - (point.Y * sin), (point.X * sin) + (point.Y * cos));
return new Point(point.X + origin.X, point.Y + origin.Y); ;
float x = point.X - origin.X;
float y = point.Y - origin.Y;
x = (x * cos) - (y * sin);
y = (x * sin) + (y * cos);
return new Point(x + origin.X, y + origin.Y); ;
}
#endregion

View File

@ -12,7 +12,7 @@ namespace RGB.NET.Core
/// <param name="rect">The rectangle to modify.</param>
/// <param name="location">The new location of the rectangle.</param>
/// <returns>The modified <see cref="Rectangle"/>.</returns>
public static Rectangle SetLocation(this Rectangle rect, Point location) => new(location, rect.Size);
public static Rectangle SetLocation(this in Rectangle rect, in Point location) => new(location, rect.Size);
/// <summary>
/// Sets the <see cref="Point.X"/> of the <see cref="Rectangle.Location"/> of the given rectangle.
@ -20,7 +20,7 @@ namespace RGB.NET.Core
/// <param name="rect">The rectangle to modify.</param>
/// <param name="x">The new x-location of the rectangle.</param>
/// <returns>The modified <see cref="Rectangle"/>.</returns>
public static Rectangle SetX(this Rectangle rect, double x) => new(new Point(x, rect.Location.Y), rect.Size);
public static Rectangle SetX(this in Rectangle rect, float x) => new(new Point(x, rect.Location.Y), rect.Size);
/// <summary>
/// Sets the <see cref="Point.Y"/> of the <see cref="Rectangle.Location"/> of the given rectangle.
@ -28,7 +28,7 @@ namespace RGB.NET.Core
/// <param name="rect">The rectangle to modify.</param>
/// <param name="y">The new y-location of the rectangle.</param>
/// <returns>The modified <see cref="Rectangle"/>.</returns>
public static Rectangle SetY(this Rectangle rect, double y) => new(new Point(rect.Location.X, y), rect.Size);
public static Rectangle SetY(this in Rectangle rect, float y) => new(new Point(rect.Location.X, y), rect.Size);
/// <summary>
/// Sets the <see cref="Rectangle.Size"/> of the given rectangle.
@ -36,7 +36,7 @@ namespace RGB.NET.Core
/// <param name="rect">The rectangle to modify.</param>
/// <param name="size">The new size of the rectangle.</param>
/// <returns>The modified <see cref="Rectangle"/>.</returns>
public static Rectangle SetSize(this Rectangle rect, Size size) => new(rect.Location, size);
public static Rectangle SetSize(this in Rectangle rect, in Size size) => new(rect.Location, size);
/// <summary>
/// Sets the <see cref="Size.Width"/> of the <see cref="Rectangle.Size"/> of the given rectangle.
@ -44,7 +44,7 @@ namespace RGB.NET.Core
/// <param name="rect">The rectangle to modify.</param>
/// <param name="width">The new width of the rectangle.</param>
/// <returns>The modified <see cref="Rectangle"/>.</returns>
public static Rectangle SetWidth(this Rectangle rect, double width) => new(rect.Location, new Size(width, rect.Size.Height));
public static Rectangle SetWidth(this in Rectangle rect, float width) => new(rect.Location, new Size(width, rect.Size.Height));
/// <summary>
/// Sets the <see cref="Size.Height"/> of the <see cref="Rectangle.Size"/> of the given rectangle.
@ -52,14 +52,14 @@ namespace RGB.NET.Core
/// <param name="rect">The rectangle to modify.</param>
/// <param name="height">The new height of the rectangle.</param>
/// <returns>The modified <see cref="Rectangle"/>.</returns>
public static Rectangle SetHeight(this Rectangle rect, double height) => new(rect.Location, new Size(rect.Size.Width, height));
public static Rectangle SetHeight(this in Rectangle rect, float height) => new(rect.Location, new Size(rect.Size.Width, height));
/// <summary>
/// Calculates the percentage of intersection of a rectangle.
/// </summary>
/// <param name="intersectingRect">The intersecting rectangle.</param>
/// <returns>The percentage of intersection.</returns>
public static double CalculateIntersectPercentage(this Rectangle rect, Rectangle intersectingRect)
public static float CalculateIntersectPercentage(this in Rectangle rect, in Rectangle intersectingRect)
{
if (rect.IsEmpty || intersectingRect.IsEmpty) return 0;
@ -72,13 +72,13 @@ namespace RGB.NET.Core
/// </summary>
/// <param name="intersectingRectangle">The intersecting <see cref="Rectangle"/></param>
/// <returns>A new <see cref="Rectangle"/> representing the intersection this <see cref="Rectangle"/> and the one provided as parameter.</returns>
public static Rectangle CalculateIntersection(this Rectangle rect, Rectangle intersectingRectangle)
public static Rectangle CalculateIntersection(this in Rectangle rect, in Rectangle intersectingRectangle)
{
double x1 = Math.Max(rect.Location.X, intersectingRectangle.Location.X);
double x2 = Math.Min(rect.Location.X + rect.Size.Width, intersectingRectangle.Location.X + intersectingRectangle.Size.Width);
float x1 = Math.Max(rect.Location.X, intersectingRectangle.Location.X);
float x2 = Math.Min(rect.Location.X + rect.Size.Width, intersectingRectangle.Location.X + intersectingRectangle.Size.Width);
double y1 = Math.Max(rect.Location.Y, intersectingRectangle.Location.Y);
double y2 = Math.Min(rect.Location.Y + rect.Size.Height, intersectingRectangle.Location.Y + intersectingRectangle.Size.Height);
float y1 = Math.Max(rect.Location.Y, intersectingRectangle.Location.Y);
float y2 = Math.Min(rect.Location.Y + rect.Size.Height, intersectingRectangle.Location.Y + intersectingRectangle.Size.Height);
if ((x2 >= x1) && (y2 >= y1))
return new Rectangle(x1, y1, x2 - x1, y2 - y1);
@ -91,7 +91,7 @@ namespace RGB.NET.Core
/// </summary>
/// <param name="point">The <see cref="Point"/> to test.</param>
/// <returns><c>true</c> if the rectangle contains the given point; otherwise <c>false</c>.</returns>
public static bool Contains(this Rectangle rect, Point point) => rect.Contains(point.X, point.Y);
public static bool Contains(this in Rectangle rect, in Point point) => rect.Contains(point.X, point.Y);
/// <summary>
/// Determines if the specified location is contained within this <see cref="Rectangle"/>.
@ -99,7 +99,7 @@ namespace RGB.NET.Core
/// <param name="x">The X-location to test.</param>
/// <param name="y">The Y-location to test.</param>
/// <returns><c>true</c> if the rectangle contains the given coordinates; otherwise <c>false</c>.</returns>
public static bool Contains(this Rectangle rect, double x, double y) => (rect.Location.X <= x) && (x < (rect.Location.X + rect.Size.Width))
public static bool Contains(this in Rectangle rect, float x, float y) => (rect.Location.X <= x) && (x < (rect.Location.X + rect.Size.Width))
&& (rect.Location.Y <= y) && (y < (rect.Location.Y + rect.Size.Height));
/// <summary>
@ -107,7 +107,7 @@ namespace RGB.NET.Core
/// </summary>
/// <param name="rect">The <see cref="Rectangle"/> to test.</param>
/// <returns><c>true</c> if the rectangle contains the given rect; otherwise <c>false</c>.</returns>
public static bool Contains(this Rectangle rect, Rectangle rect2) => (rect.Location.X <= rect2.Location.X) && ((rect2.Location.X + rect2.Size.Width) <= (rect.Location.X + rect.Size.Width))
public static bool Contains(this in Rectangle rect, in Rectangle rect2) => (rect.Location.X <= rect2.Location.X) && ((rect2.Location.X + rect2.Size.Width) <= (rect.Location.X + rect.Size.Width))
&& (rect.Location.Y <= rect2.Location.Y) && ((rect2.Location.Y + rect2.Size.Height) <= (rect.Location.Y + rect.Size.Height));
/// <summary>
@ -116,7 +116,7 @@ namespace RGB.NET.Core
/// <param name="rect">The <see cref="Rectangle"/> to move.</param>
/// <param name="point">The amount to move.</param>
/// <returns>The moved rectangle.</returns>
public static Rectangle Translate(this Rectangle rect, Point point) => rect.Translate(point.X, point.Y);
public static Rectangle Translate(this in Rectangle rect, in Point point) => rect.Translate(point.X, point.Y);
/// <summary>
/// Moves the specified <see cref="Rectangle"/> by the given amount.
@ -125,7 +125,7 @@ namespace RGB.NET.Core
/// <param name="x">The x-ammount to move.</param>
/// <param name="y">The y-ammount to move.</param>
/// <returns>The moved rectangle.</returns>
public static Rectangle Translate(this Rectangle rect, double x = 0, double y = 0) => new(rect.Location.Translate(x, y), rect.Size);
public static Rectangle Translate(this in Rectangle rect, float x = 0, float y = 0) => new(rect.Location.Translate(x, y), rect.Size);
/// <summary>
/// Rotates the specified <see cref="Rectangle"/> by the given amuont around the given origin.
@ -141,7 +141,7 @@ namespace RGB.NET.Core
/// <param name="rotation">The rotation.</param>
/// <param name="origin">The origin to rotate around. [0,0] if not set.</param>
/// <returns>A array of <see cref="Point"/> containing the new locations of the corners of the original rectangle.</returns>
public static Point[] Rotate(this Rectangle rect, Rotation rotation, Point origin = new())
public static Point[] Rotate(this in Rectangle rect, in Rotation rotation, in Point origin = new())
{
Point[] points = {
rect.Location, // top left
@ -150,8 +150,8 @@ namespace RGB.NET.Core
new(rect.Location.X, rect.Location.Y + rect.Size.Height), // bottom right
};
double sin = Math.Sin(rotation.Radians);
double cos = Math.Cos(rotation.Radians);
float sin = MathF.Sin(rotation.Radians);
float cos = MathF.Cos(rotation.Radians);
for (int i = 0; i < points.Length; i++)
{

View File

@ -0,0 +1,65 @@
// ReSharper disable UnusedMember.Global
using System.Collections.Generic;
using System.Linq;
namespace RGB.NET.Core
{
public static class SurfaceExtensions
{
#region Methods
public static void Load(this RGBSurface surface, IRGBDeviceProvider deviceProvider, RGBDeviceType loadFilter = RGBDeviceType.All, bool throwExceptions = false)
{
if (!deviceProvider.IsInitialized)
deviceProvider.Initialize(loadFilter, throwExceptions);
surface.Attach(deviceProvider.Devices);
}
public static void Attach(this RGBSurface surface, IEnumerable<IRGBDevice> devices)
{
foreach (IRGBDevice device in devices)
surface.Attach(device);
}
public static void Detach(this RGBSurface surface, IEnumerable<IRGBDevice> devices)
{
foreach (IRGBDevice device in devices)
surface.Detach(device);
}
/// <summary>
/// Gets all devices of a specific type.
/// </summary>
/// <typeparam name="T">The type of devices to get.</typeparam>
/// <returns>A collection of devices with the specified type.</returns>
public static IEnumerable<T> GetDevices<T>(this RGBSurface surface)
where T : class
=> surface.Devices.Where(x => x is T).Cast<T>();
/// <summary>
/// Gets all devices of the specified <see cref="RGBDeviceType"/>.
/// </summary>
/// <param name="deviceType">The <see cref="RGBDeviceType"/> of the devices to get.</param>
/// <returns>A collection of devices matching the specified <see cref="RGBDeviceType"/>.</returns>
public static IEnumerable<IRGBDevice> GetDevices(this RGBSurface surface, RGBDeviceType deviceType)
=> surface.Devices.Where(d => deviceType.HasFlag(d.DeviceInfo.DeviceType));
/// <summary>
/// Automatically aligns all devices to prevent overlaps.
/// </summary>
public static void AlignDevices(this RGBSurface surface)
{
float posX = 0;
foreach (IRGBDevice device in surface.Devices)
{
device.Location += new Point(posX, 0);
posX += device.ActualSize.Width + 1;
}
}
#endregion
}
}

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Collections;
using System.Collections.Generic;
namespace RGB.NET.Core
{
@ -11,7 +12,8 @@ namespace RGB.NET.Core
{
#region Properties & Fields
public RGBSurface? Surface { get; private set; }
RGBSurface? ILedGroup.Surface { get; set; }
public RGBSurface? Surface => ((ILedGroup)this).Surface;
/// <inheritdoc />
public IBrush? Brush { get; set; }
@ -28,21 +30,26 @@ namespace RGB.NET.Core
/// </summary>
protected AbstractLedGroup(RGBSurface? attachTo)
{
attachTo?.AttachLedGroup(this);
attachTo?.Attach(this);
}
#endregion
#region Methods
/// <inheritdoc />
public abstract IList<Led> GetLeds();
protected abstract IEnumerable<Led> GetLeds();
/// <inheritdoc />
public virtual void OnAttach(RGBSurface surface) => Surface = surface;
public virtual void OnAttach() { }
/// <inheritdoc />
public virtual void OnDetach(RGBSurface surface) => Surface = null;
public virtual void OnDetach() { }
/// <inheritdoc />
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
/// <inheritdoc />
public IEnumerator<Led> GetEnumerator() => GetLeds().GetEnumerator();
#endregion
}

View File

@ -5,13 +5,14 @@ using System.Collections.Generic;
namespace RGB.NET.Core
{
/// <inheritdoc />
/// <summary>
/// Represents a generic ledgroup.
/// </summary>
public interface ILedGroup : IDecoratable<ILedGroupDecorator>
public interface ILedGroup : IDecoratable<ILedGroupDecorator>, IEnumerable<Led>
{
public RGBSurface? Surface { get; }
RGBSurface? Surface { get; internal set; }
bool IsAttached => Surface != null;
/// <summary>
/// Gets or sets the <see cref="IBrush"/> which should be drawn over this <see cref="ILedGroup"/>.
@ -23,20 +24,14 @@ namespace RGB.NET.Core
/// </summary>
int ZIndex { get; set; }
/// <summary>
/// Gets a list containing all <see cref="Led"/> of this <see cref="ILedGroup"/>.
/// </summary>
/// <returns>The list containing all <see cref="Led"/> of this <see cref="ILedGroup"/>.</returns>
IList<Led> GetLeds();
/// <summary>
/// Called when the <see cref="ILedGroup"/> is attached to the <see cref="RGBSurface"/>.
/// </summary>
void OnAttach(RGBSurface surface);
void OnAttach();
/// <summary>
/// Called when the <see cref="ILedGroup"/> is detached from the <see cref="RGBSurface"/>.
/// </summary>
void OnDetach(RGBSurface surface);
void OnDetach();
}
}

View File

@ -1,9 +1,7 @@
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable UnusedMember.Global
using RGB.NET.Core;
namespace RGB.NET.Groups
namespace RGB.NET.Core
{
/// <summary>
/// Offers some extensions and helper-methods for <see cref="ILedGroup"/> related things.
@ -18,11 +16,11 @@ namespace RGB.NET.Groups
public static ListLedGroup ToListLedGroup(this ILedGroup ledGroup)
{
// ReSharper disable once InvertIf
if (!(ledGroup is ListLedGroup listLedGroup))
if (ledGroup is not ListLedGroup listLedGroup)
{
if (ledGroup.Surface != null)
ledGroup.Detach(ledGroup.Surface);
listLedGroup = new ListLedGroup(ledGroup.Surface, ledGroup.GetLeds()) { Brush = ledGroup.Brush };
if (ledGroup.IsAttached)
ledGroup.Detach();
listLedGroup = new ListLedGroup(ledGroup.Surface, ledGroup) { Brush = ledGroup.Brush, ZIndex = ledGroup.ZIndex };
}
return listLedGroup;
}
@ -47,13 +45,13 @@ namespace RGB.NET.Groups
/// </summary>
/// <param name="ledGroup">The <see cref="ILedGroup"/> to attach.</param>
/// <returns><c>true</c> if the <see cref="ILedGroup"/> could be attached; otherwise, <c>false</c>.</returns>
public static bool Attach(this ILedGroup ledGroup, RGBSurface surface) => surface.AttachLedGroup(ledGroup);
public static bool Attach(this ILedGroup ledGroup, RGBSurface surface) => surface.Attach(ledGroup);
/// <summary>
/// Detaches the given <see cref="ILedGroup"/> from the <see cref="RGBSurface"/>.
/// </summary>
/// <param name="ledGroup">The <see cref="ILedGroup"/> to attach.</param>
/// <returns><c>true</c> if the <see cref="ILedGroup"/> could be detached; otherwise, <c>false</c>.</returns>
public static bool Detach(this ILedGroup ledGroup, RGBSurface surface) => surface.DetachLedGroup(ledGroup);
public static bool Detach(this ILedGroup ledGroup) => ledGroup.Surface?.Detach(ledGroup) ?? false;
}
}

View File

@ -2,9 +2,8 @@
// ReSharper disable UnusedMember.Global
using System.Collections.Generic;
using RGB.NET.Core;
namespace RGB.NET.Groups
namespace RGB.NET.Core
{
/// <inheritdoc />
/// <summary>
@ -114,7 +113,7 @@ namespace RGB.NET.Groups
public void MergeLeds(ILedGroup groupToMerge)
{
lock (GroupLeds)
foreach (Led led in groupToMerge.GetLeds())
foreach (Led led in groupToMerge)
if (!GroupLeds.Contains(led))
GroupLeds.Add(led);
}
@ -124,7 +123,7 @@ namespace RGB.NET.Groups
/// Gets a list containing the <see cref="T:RGB.NET.Core.Led" /> from this group.
/// </summary>
/// <returns>The list containing the <see cref="T:RGB.NET.Core.Led" />.</returns>
public override IList<Led> GetLeds()
protected override IEnumerable<Led> GetLeds()
{
lock (GroupLeds)
return new List<Led>(GroupLeds);

View File

@ -151,18 +151,6 @@ namespace RGB.NET.Core
OnPropertyChanged(nameof(Color));
}
/// <summary>
/// Resets the <see cref="Led"/> back to default.
/// </summary>
internal void Reset()
{
_color = Color.Transparent;
RequestedColor = null;
// ReSharper disable once ExplicitCallerInfoArgument
OnPropertyChanged(nameof(Color));
}
#endregion
#region Operators

View File

@ -13,10 +13,11 @@ namespace RGB.NET.Core
{
#region Constants
private static readonly Point INVALID = new(float.NaN, float.NaN);
/// <summary>
/// Gets a [NaN,NaN]-Point.
/// </summary>
public static Point Invalid => new(double.NaN, double.NaN);
public static ref readonly Point Invalid => ref INVALID;
#endregion
@ -25,12 +26,12 @@ namespace RGB.NET.Core
/// <summary>
/// Gets the X-position of this <see cref="Point"/>.
/// </summary>
public double X { get; }
public float X { get; }
/// <summary>
/// Gets the Y-position of this <see cref="Point"/>.
/// </summary>
public double Y { get; }
public float Y { get; }
#endregion
@ -41,7 +42,7 @@ namespace RGB.NET.Core
/// </summary>
/// <param name="x">The value used for the X-position.</param>
/// <param name="y">The value used for the Y-position.</param>
public Point(double x, double y)
public Point(float x, float y)
{
this.X = x;
this.Y = y;
@ -67,8 +68,8 @@ namespace RGB.NET.Core
if (!(obj is Point)) return false;
Point comparePoint = (Point)obj;
return ((double.IsNaN(X) && double.IsNaN(comparePoint.X)) || X.EqualsInTolerance(comparePoint.X))
&& ((double.IsNaN(Y) && double.IsNaN(comparePoint.Y)) || Y.EqualsInTolerance(comparePoint.Y));
return ((float.IsNaN(X) && float.IsNaN(comparePoint.X)) || X.EqualsInTolerance(comparePoint.X))
&& ((float.IsNaN(Y) && float.IsNaN(comparePoint.Y)) || Y.EqualsInTolerance(comparePoint.Y));
}
/// <summary>
@ -95,7 +96,7 @@ namespace RGB.NET.Core
/// <param name="point1">The first <see cref="Point" /> to compare.</param>
/// <param name="point2">The second <see cref="Point" /> to compare.</param>
/// <returns><c>true</c> if <paramref name="point1" /> and <paramref name="point2" /> are equal; otherwise, <c>false</c>.</returns>
public static bool operator ==(Point point1, Point point2) => point1.Equals(point2);
public static bool operator ==(in Point point1, in Point point2) => point1.Equals(point2);
/// <summary>
/// Returns a value that indicates whether two specified <see cref="Point" /> are equal.
@ -103,7 +104,7 @@ namespace RGB.NET.Core
/// <param name="point1">The first <see cref="Point" /> to compare.</param>
/// <param name="point2">The second <see cref="Point" /> to compare.</param>
/// <returns><c>true</c> if <paramref name="point1" /> and <paramref name="point2" /> are not equal; otherwise, <c>false</c>.</returns>
public static bool operator !=(Point point1, Point point2) => !(point1 == point2);
public static bool operator !=(in Point point1, in Point point2) => !(point1 == point2);
/// <summary>
/// Returns a new <see cref="Point"/> representing the addition of the two provided <see cref="Point"/>.
@ -111,7 +112,7 @@ namespace RGB.NET.Core
/// <param name="point1">The first <see cref="Point"/>.</param>
/// <param name="point2">The second <see cref="Point"/>.</param>
/// <returns>A new <see cref="Point"/> representing the addition of the two provided <see cref="Point"/>.</returns>
public static Point operator +(Point point1, Point point2) => new(point1.X + point2.X, point1.Y + point2.Y);
public static Point operator +(in Point point1, in Point point2) => new(point1.X + point2.X, point1.Y + point2.Y);
/// <summary>
/// Returns a new <see cref="Rectangle"/> created from the provided <see cref="Point"/> and <see cref="Size"/>.
@ -119,7 +120,7 @@ namespace RGB.NET.Core
/// <param name="point">The <see cref="Point"/> of the rectangle.</param>
/// <param name="size">The <see cref="Size"/> of the rectangle.</param>
/// <returns>The rectangle created from the provided <see cref="Point"/> and <see cref="Size"/>.</returns>
public static Rectangle operator +(Point point, Size size) => new(point, size);
public static Rectangle operator +(in Point point, in Size size) => new(point, size);
/// <summary>
/// Returns a new <see cref="Point"/> representing the subtraction of the two provided <see cref="Point"/>.
@ -127,7 +128,7 @@ namespace RGB.NET.Core
/// <param name="point1">The first <see cref="Point"/>.</param>
/// <param name="point2">The second <see cref="Point"/>.</param>
/// <returns>A new <see cref="Point"/> representing the subtraction of the two provided <see cref="Point"/>.</returns>
public static Point operator -(Point point1, Point point2) => new(point1.X - point2.X, point1.Y - point2.Y);
public static Point operator -(in Point point1, in Point point2) => new(point1.X - point2.X, point1.Y - point2.Y);
/// <summary>
/// Returns a new <see cref="Point"/> representing the multiplication of the two provided <see cref="Point"/>.
@ -135,7 +136,7 @@ namespace RGB.NET.Core
/// <param name="point1">The first <see cref="Point"/>.</param>
/// <param name="point2">The second <see cref="Point"/>.</param>
/// <returns>A new <see cref="Point"/> representing the multiplication of the two provided <see cref="Point"/>.</returns>
public static Point operator *(Point point1, Point point2) => new(point1.X * point2.X, point1.Y * point2.Y);
public static Point operator *(in Point point1, in Point point2) => new(point1.X * point2.X, point1.Y * point2.Y);
/// <summary>
/// Returns a new <see cref="Point"/> representing the division of the two provided <see cref="Point"/>.
@ -143,7 +144,7 @@ namespace RGB.NET.Core
/// <param name="point1">The first <see cref="Point"/>.</param>
/// <param name="point2">The second <see cref="Point"/>.</param>
/// <returns>A new <see cref="Point"/> representing the division of the two provided <see cref="Point"/>.</returns>
public static Point operator /(Point point1, Point point2)
public static Point operator /(in Point point1, in Point point2)
{
if (point2.X.EqualsInTolerance(0) || point2.Y.EqualsInTolerance(0)) return Invalid;
return new Point(point1.X / point2.X, point1.Y / point2.Y);
@ -155,7 +156,7 @@ namespace RGB.NET.Core
/// <param name="point">The <see cref="Point"/>.</param>
/// <param name="scale">The <see cref="Scale"/>.</param>
/// <returns>A new <see cref="Point"/> representing the multiplication of the <see cref="Point"/> and the provided <see cref="Scale"/>.</returns>
public static Point operator *(Point point, Scale scale) => new(point.X * scale.Horizontal, point.Y * scale.Vertical);
public static Point operator *(in Point point, in Scale scale) => new(point.X * scale.Horizontal, point.Y * scale.Vertical);
#endregion
}

View File

@ -35,7 +35,7 @@ namespace RGB.NET.Core
/// Gets a bool indicating if both, the width and the height of the rectangle is greater than zero.
/// <c>True</c> if the rectangle has a width or a height of zero; otherwise, <c>false</c>.
/// </summary>
public bool IsEmpty => (Size.Width <= DoubleExtensions.TOLERANCE) || (Size.Height <= DoubleExtensions.TOLERANCE);
public bool IsEmpty => (Size.Width <= FloatExtensions.TOLERANCE) || (Size.Height <= FloatExtensions.TOLERANCE);
#endregion
@ -49,7 +49,7 @@ namespace RGB.NET.Core
/// <param name="y">The y-value of the <see cref="T:RGB.NET.Core.Location" />-position of this <see cref="T:RGB.NET.Core.Rectangle" />.</param>
/// <param name="width">The width of the <see cref="T:RGB.NET.Core.Size"/> of this <see cref="T:RGB.NET.Core.Rectangle" />.</param>
/// <param name="height">The height of the <see cref="T:RGB.NET.Core.Size"/> of this <see cref="T:RGB.NET.Core.Rectangle" />.</param>
public Rectangle(double x, double y, double width, double height)
public Rectangle(float x, float y, float width, float height)
: this(new Point(x, y), new Size(width, height))
{ }
@ -70,7 +70,7 @@ namespace RGB.NET.Core
this.Location = location;
this.Size = size;
Center = new Point(Location.X + (Size.Width / 2.0), Location.Y + (Size.Height / 2.0));
Center = new Point(Location.X + (Size.Width / 2.0f), Location.Y + (Size.Height / 2.0f));
}
/// <inheritdoc />
@ -91,10 +91,10 @@ namespace RGB.NET.Core
public Rectangle(IEnumerable<Rectangle> rectangles)
{
bool hasPoint = false;
double posX = double.MaxValue;
double posY = double.MaxValue;
double posX2 = double.MinValue;
double posY2 = double.MinValue;
float posX = float.MaxValue;
float posY = float.MaxValue;
float posX2 = float.MinValue;
float posY2 = float.MinValue;
foreach (Rectangle rectangle in rectangles)
{
@ -108,7 +108,7 @@ namespace RGB.NET.Core
(Point location, Size size) = hasPoint ? InitializeFromPoints(new Point(posX, posY), new Point(posX2, posY2)) : InitializeFromPoints(new Point(0, 0), new Point(0, 0));
Location = location;
Size = size;
Center = new Point(Location.X + (Size.Width / 2.0), Location.Y + (Size.Height / 2.0));
Center = new Point(Location.X + (Size.Width / 2.0f), Location.Y + (Size.Height / 2.0f));
}
/// <inheritdoc />
@ -131,10 +131,10 @@ namespace RGB.NET.Core
: this()
{
bool hasPoint = false;
double posX = double.MaxValue;
double posY = double.MaxValue;
double posX2 = double.MinValue;
double posY2 = double.MinValue;
float posX = float.MaxValue;
float posY = float.MaxValue;
float posX2 = float.MinValue;
float posY2 = float.MinValue;
foreach (Point point in points)
{
@ -149,19 +149,19 @@ namespace RGB.NET.Core
Location = location;
Size = size;
Center = new Point(Location.X + (Size.Width / 2.0), Location.Y + (Size.Height / 2.0));
Center = new Point(Location.X + (Size.Width / 2.0f), Location.Y + (Size.Height / 2.0f));
}
#endregion
#region Methods
private static (Point location, Size size) InitializeFromPoints(Point point1, Point point2)
private static (Point location, Size size) InitializeFromPoints(in Point point1, in Point point2)
{
double posX = Math.Min(point1.X, point2.X);
double posY = Math.Min(point1.Y, point2.Y);
double width = Math.Max(point1.X, point2.X) - posX;
double height = Math.Max(point1.Y, point2.Y) - posY;
float posX = Math.Min(point1.X, point2.X);
float posY = Math.Min(point1.Y, point2.Y);
float width = Math.Max(point1.X, point2.X) - posX;
float height = Math.Max(point1.Y, point2.Y) - posY;
return (new Point(posX, posY), new Size(width, height));
}
@ -179,7 +179,7 @@ namespace RGB.NET.Core
/// <returns><c>true</c> if <paramref name="obj" /> is a <see cref="Rectangle" /> equivalent to this <see cref="Rectangle" />; otherwise, <c>false</c>.</returns>
public override bool Equals(object? obj)
{
if (!(obj is Rectangle compareRect))
if (obj is not Rectangle compareRect)
return false;
if (GetType() != compareRect.GetType())
@ -212,7 +212,7 @@ namespace RGB.NET.Core
/// <param name="rectangle1">The first <see cref="Rectangle" /> to compare.</param>
/// <param name="rectangle2">The second <see cref="Rectangle" /> to compare.</param>
/// <returns><c>true</c> if <paramref name="rectangle1" /> and <paramref name="rectangle2" /> are equal; otherwise, <c>false</c>.</returns>
public static bool operator ==(Rectangle rectangle1, Rectangle rectangle2) => rectangle1.Equals(rectangle2);
public static bool operator ==(in Rectangle rectangle1, in Rectangle rectangle2) => rectangle1.Equals(rectangle2);
/// <summary>
/// Returns a value that indicates whether two specified <see cref="Rectangle" /> are equal.
@ -220,7 +220,16 @@ namespace RGB.NET.Core
/// <param name="rectangle1">The first <see cref="Rectangle" /> to compare.</param>
/// <param name="rectangle2">The second <see cref="Rectangle" /> to compare.</param>
/// <returns><c>true</c> if <paramref name="rectangle1" /> and <paramref name="rectangle2" /> are not equal; otherwise, <c>false</c>.</returns>
public static bool operator !=(Rectangle rectangle1, Rectangle rectangle2) => !(rectangle1 == rectangle2);
public static bool operator !=(in Rectangle rectangle1, in Rectangle rectangle2) => !(rectangle1 == rectangle2);
// DarthAffe 20.02.2021: Used for normalization
public static Rectangle operator /(in Rectangle rectangle1, in Rectangle rectangle2)
{
float x = rectangle1.Location.X / (rectangle2.Size.Width - rectangle2.Location.X);
float y = rectangle1.Location.Y / (rectangle2.Size.Height - rectangle2.Location.Y);
Size size = rectangle1.Size / rectangle2.Size;
return new Rectangle(new Point(x, y), size);
}
#endregion
}

View File

@ -14,9 +14,9 @@ namespace RGB.NET.Core
{
#region Constants
private const double TWO_PI = Math.PI * 2.0;
private const double RADIANS_DEGREES_CONVERSION = 180.0 / Math.PI;
private const double DEGREES_RADIANS_CONVERSION = Math.PI / 180.0;
private const float TWO_PI = MathF.PI * 2.0f;
private const float RADIANS_DEGREES_CONVERSION = 180.0f / MathF.PI;
private const float DEGREES_RADIANS_CONVERSION = MathF.PI / 180.0f;
#endregion
@ -25,12 +25,12 @@ namespace RGB.NET.Core
/// <summary>
/// Gets the angle in degrees.
/// </summary>
public double Degrees { get; }
public float Degrees { get; }
/// <summary>
/// Gets the angle in radians.
/// </summary>
public double Radians { get; }
public float Radians { get; }
/// <summary>
/// Gets a bool indicating if the rotation is > 0.
@ -45,13 +45,13 @@ namespace RGB.NET.Core
/// Initializes a new instance of the <see cref="Rotation"/> class using the provided values.
/// </summary>
/// <param name="scale">The rotation in degrees.</param>
public Rotation(double degrees)
public Rotation(float degrees)
: this(degrees, degrees * DEGREES_RADIANS_CONVERSION)
{ }
private Rotation(double degrees, double radians)
private Rotation(float degrees, float radians)
{
this.Degrees = degrees % 360.0;
this.Degrees = degrees % 360.0f;
this.Radians = radians % TWO_PI;
}
@ -64,14 +64,14 @@ namespace RGB.NET.Core
/// </summary>
/// <param name="degrees">The angle in degrees.</param>
/// <returns>The new rotation.</returns>
public static Rotation FromDegrees(double degrees) => new(degrees);
public static Rotation FromDegrees(float degrees) => new(degrees);
/// <summary>
/// Creates a new Rotation out of the given radian-angle.
/// </summary>
/// <param name="degrees">The angle in radians.</param>
/// <returns>The new rotation.</returns>
public static Rotation FromRadians(double radians) => new(radians * RADIANS_DEGREES_CONVERSION, radians);
public static Rotation FromRadians(float radians) => new(radians * RADIANS_DEGREES_CONVERSION, radians);
/// <summary>
/// Tests whether the specified <see cref="Rotation" /> is equivalent to this <see cref="Rotation" />.
@ -103,7 +103,7 @@ namespace RGB.NET.Core
/// <param name="rotation1">The first <see cref="Rotation" /> to compare.</param>
/// <param name="rotation2">The second <see cref="Rotation" /> to compare.</param>
/// <returns><c>true</c> if <paramref name="rotation1" /> and <paramref name="rotation2" /> are equal; otherwise, <c>false</c>.</returns>
public static bool operator ==(Rotation rotation1, Rotation rotation2) => rotation1.Equals(rotation2);
public static bool operator ==(in Rotation rotation1, in Rotation rotation2) => rotation1.Equals(rotation2);
/// <summary>
/// Returns a value that indicates whether two specified <see cref="Rotation" /> are equal.
@ -111,7 +111,7 @@ namespace RGB.NET.Core
/// <param name="rotation1">The first <see cref="Rotation" /> to compare.</param>
/// <param name="rotation2">The second <see cref="Rotation" /> to compare.</param>
/// <returns><c>true</c> if <paramref name="rotation1" /> and <paramref name="rotation2" /> are not equal; otherwise, <c>false</c>.</returns>
public static bool operator !=(Rotation rotation1, Rotation rotation2) => !(rotation1 == rotation2);
public static bool operator !=(in Rotation rotation1, in Rotation rotation2) => !(rotation1 == rotation2);
/// <summary>
/// Returns a new <see cref="Rotation"/> representing the addition of the <see cref="Rotation"/> and the provided value.
@ -119,7 +119,7 @@ namespace RGB.NET.Core
/// <param name="rotation">The <see cref="Rotation"/>.</param>
/// <param name="value">The value to add.</param>
/// <returns>A new <see cref="Rotation"/> representing the addition of the <see cref="Rotation"/> and the provided value.</returns>
public static Rotation operator +(Rotation rotation, double value) => new(rotation.Degrees + value);
public static Rotation operator +(in Rotation rotation, float value) => new(rotation.Degrees + value);
/// <summary>
/// Returns a new <see cref="Rotation"/> representing the subtraction of the <see cref="Rotation"/> and the provided value.
@ -127,7 +127,7 @@ namespace RGB.NET.Core
/// <param name="rotation">The <see cref="Rotation"/>.</param>
/// <param name="value">The value to substract.</param>
/// <returns>A new <see cref="Rotation"/> representing the subtraction of the <see cref="Rotation"/> and the provided value.</returns>
public static Rotation operator -(Rotation rotation, double value) => new(rotation.Degrees - value);
public static Rotation operator -(in Rotation rotation, float value) => new(rotation.Degrees - value);
/// <summary>
/// Returns a new <see cref="Rotation"/> representing the multiplication of the <see cref="Rotation"/> and the provided value.
@ -135,7 +135,7 @@ namespace RGB.NET.Core
/// <param name="rotation">The <see cref="Rotation"/>.</param>
/// <param name="value">The value to multiply with.</param>
/// <returns>A new <see cref="Rotation"/> representing the multiplication of the <see cref="Rotation"/> and the provided value.</returns>
public static Rotation operator *(Rotation rotation, double value) => new(rotation.Degrees * value);
public static Rotation operator *(in Rotation rotation, float value) => new(rotation.Degrees * value);
/// <summary>
/// Returns a new <see cref="Rotation"/> representing the division of the <see cref="Rotation"/> and the provided value.
@ -143,19 +143,19 @@ namespace RGB.NET.Core
/// <param name="rotation">The <see cref="Rotation"/>.</param>
/// <param name="value">The value to device with.</param>
/// <returns>A new <see cref="Rotation"/> representing the division of the <see cref="Rotation"/> and the provided value.</returns>
public static Rotation operator /(Rotation rotation, double value) => value.EqualsInTolerance(0) ? new Rotation(0) : new Rotation(rotation.Degrees / value);
public static Rotation operator /(in Rotation rotation, float value) => value.EqualsInTolerance(0) ? new Rotation(0) : new Rotation(rotation.Degrees / value);
/// <summary>
/// Converts a double to a <see cref="Rotation" />.
/// Converts a float to a <see cref="Rotation" />.
/// </summary>
/// <param name="rotation">The rotation in degrees to convert.</param>
public static implicit operator Rotation(double rotation) => new(rotation);
public static implicit operator Rotation(float rotation) => new(rotation);
/// <summary>
/// Converts <see cref="Rotation" /> to a double representing the rotation in degrees.
/// Converts <see cref="Rotation" /> to a float representing the rotation in degrees.
/// </summary>
/// <param name="rotation">The rotatio to convert.</param>
public static implicit operator double(Rotation rotation) => rotation.Degrees;
public static implicit operator float(in Rotation rotation) => rotation.Degrees;
#endregion
}

View File

@ -16,12 +16,12 @@ namespace RGB.NET.Core
/// <summary>
/// Gets the horizontal scaling value.
/// </summary>
public double Horizontal { get; }
public float Horizontal { get; }
/// <summary>
/// Gets the vertical scaling value.
/// </summary>
public double Vertical { get; }
public float Vertical { get; }
#endregion
@ -31,7 +31,7 @@ namespace RGB.NET.Core
/// Initializes a new instance of the <see cref="Scale"/> class using the provided values.
/// </summary>
/// <param name="scale">The value used for horizontal and vertical scaling. 0 if not set.</param>
public Scale(double scale = 1.0) : this(scale, scale)
public Scale(float scale = 1.0f) : this(scale, scale)
{ }
/// <summary>
@ -39,7 +39,7 @@ namespace RGB.NET.Core
/// </summary>
/// <param name="horizontal">The value used for horizontal scaling.</param>
/// <param name="vertical">The value used for vertical scaling.</param>
public Scale(double horizontal, double vertical)
public Scale(float horizontal, float vertical)
{
this.Horizontal = horizontal;
this.Vertical = vertical;
@ -74,7 +74,7 @@ namespace RGB.NET.Core
/// </summary>
/// <param name="horizontalScale">The horizontal scaling value.</param>
/// <param name="verticalScale">The vertical scaling value.</param>
public void Deconstruct(out double horizontalScale, out double verticalScale)
public void Deconstruct(out float horizontalScale, out float verticalScale)
{
horizontalScale = Horizontal;
verticalScale = Vertical;
@ -106,7 +106,7 @@ namespace RGB.NET.Core
/// <param name="scale">The <see cref="Scale"/>.</param>
/// <param name="value">The value to add.</param>
/// <returns>A new <see cref="Scale"/> representing the addition of the <see cref="Scale"/> and the provided value.</returns>
public static Scale operator +(Scale scale, double value) => new(scale.Horizontal + value, scale.Vertical + value);
public static Scale operator +(Scale scale, float value) => new(scale.Horizontal + value, scale.Vertical + value);
/// <summary>
/// Returns a new <see cref="Scale"/> representing the subtraction of the <see cref="Scale"/> and the provided value.
@ -114,7 +114,7 @@ namespace RGB.NET.Core
/// <param name="scale">The <see cref="Scale"/>.</param>
/// <param name="value">The value to substract.</param>
/// <returns>A new <see cref="Scale"/> representing the subtraction of the <see cref="Scale"/> and the provided value.</returns>
public static Scale operator -(Scale scale, double value) => new(scale.Horizontal - value, scale.Vertical - value);
public static Scale operator -(Scale scale, float value) => new(scale.Horizontal - value, scale.Vertical - value);
/// <summary>
/// Returns a new <see cref="Scale"/> representing the multiplication of the <see cref="Scale"/> and the provided value.
@ -122,7 +122,7 @@ namespace RGB.NET.Core
/// <param name="scale">The <see cref="Scale"/>.</param>
/// <param name="value">The value to multiply with.</param>
/// <returns>A new <see cref="Scale"/> representing the multiplication of the <see cref="Scale"/> and the provided value.</returns>
public static Scale operator *(Scale scale, double value) => new(scale.Horizontal * value, scale.Vertical * value);
public static Scale operator *(Scale scale, float value) => new(scale.Horizontal * value, scale.Vertical * value);
/// <summary>
/// Returns a new <see cref="Scale"/> representing the division of the <see cref="Scale"/> and the provided value.
@ -130,14 +130,14 @@ namespace RGB.NET.Core
/// <param name="scale">The <see cref="Scale"/>.</param>
/// <param name="value">The value to device with.</param>
/// <returns>A new <see cref="Scale"/> representing the division of the <see cref="Scale"/> and the provided value.</returns>
public static Scale operator /(Scale scale, double value) => value.EqualsInTolerance(0) ? new Scale(0) : new Scale(scale.Horizontal / value, scale.Vertical / value);
public static Scale operator /(Scale scale, float value) => value.EqualsInTolerance(0) ? new Scale(0) : new Scale(scale.Horizontal / value, scale.Vertical / value);
/// <summary>
/// Converts a double to a <see cref="Scale" />.
/// Converts a float to a <see cref="Scale" />.
/// </summary>
/// <param name="scale">The scale value to convert.</param>
public static implicit operator Scale(double scale) => new(scale);
public static implicit operator Scale(float scale) => new(scale);
#endregion
}

View File

@ -13,10 +13,11 @@ namespace RGB.NET.Core
{
#region Constants
private static readonly Size INVALID = new(float.NaN, float.NaN);
/// <summary>
/// Gets a [NaN,NaN]-Size.
/// </summary>
public static Size Invalid => new(double.NaN, double.NaN);
public static ref readonly Size Invalid => ref INVALID;
#endregion
@ -25,12 +26,12 @@ namespace RGB.NET.Core
/// <summary>
/// Gets or sets the width component value of this <see cref="Size"/>.
/// </summary>
public double Width { get; }
public float Width { get; }
/// <summary>
/// Gets or sets the height component value of this <see cref="Size"/>.
/// </summary>
public double Height { get; }
public float Height { get; }
#endregion
@ -41,7 +42,7 @@ namespace RGB.NET.Core
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.Size" /> using the provided size to define a square.
/// </summary>
/// <param name="size">The size used for the <see cref="P:RGB.NET.Core.Size.Width" /> component value and the <see cref="P:RGB.NET.Core.Size.Height" /> component value.</param>
public Size(double size)
public Size(float size)
: this(size, size)
{ }
@ -50,7 +51,7 @@ namespace RGB.NET.Core
/// </summary>
/// <param name="width">The size used for the <see cref="Width"/> component value.</param>
/// <param name="height">The size used for the <see cref="Height"/> component value.</param>
public Size(double width, double height)
public Size(float width, float height)
{
this.Width = width;
this.Height = height;
@ -75,9 +76,9 @@ namespace RGB.NET.Core
{
if (!(obj is Size size)) return false;
(double width, double height) = size;
return ((double.IsNaN(Width) && double.IsNaN(width)) || Width.EqualsInTolerance(width))
&& ((double.IsNaN(Height) && double.IsNaN(height)) || Height.EqualsInTolerance(height));
(float width, float height) = size;
return ((float.IsNaN(Width) && float.IsNaN(width)) || Width.EqualsInTolerance(width))
&& ((float.IsNaN(Height) && float.IsNaN(height)) || Height.EqualsInTolerance(height));
}
/// <summary>
@ -99,7 +100,7 @@ namespace RGB.NET.Core
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
public void Deconstruct(out double width, out double height)
public void Deconstruct(out float width, out float height)
{
width = Width;
height = Height;
@ -115,7 +116,7 @@ namespace RGB.NET.Core
/// <param name="size1">The first <see cref="Size" /> to compare.</param>
/// <param name="size2">The second <see cref="Size" /> to compare.</param>
/// <returns><c>true</c> if <paramref name="size1" /> and <paramref name="size2" /> are equal; otherwise, <c>false</c>.</returns>
public static bool operator ==(Size size1, Size size2) => size1.Equals(size2);
public static bool operator ==(in Size size1, in Size size2) => size1.Equals(size2);
/// <summary>
/// Returns a value that indicates whether two specified <see cref="Size" /> are equal.
@ -123,7 +124,7 @@ namespace RGB.NET.Core
/// <param name="size1">The first <see cref="Size" /> to compare.</param>
/// <param name="size2">The second <see cref="Size" /> to compare.</param>
/// <returns><c>true</c> if <paramref name="size1" /> and <paramref name="size2" /> are not equal; otherwise, <c>false</c>.</returns>
public static bool operator !=(Size size1, Size size2) => !(size1 == size2);
public static bool operator !=(in Size size1, in Size size2) => !(size1 == size2);
/// <summary>
/// Returns a new <see cref="Size"/> representing the addition of the two provided <see cref="Size"/>.
@ -131,7 +132,7 @@ namespace RGB.NET.Core
/// <param name="size1">The first <see cref="Size"/>.</param>
/// <param name="size2">The second <see cref="Size"/>.</param>
/// <returns>A new <see cref="Size"/> representing the addition of the two provided <see cref="Size"/>.</returns>
public static Size operator +(Size size1, Size size2) => new(size1.Width + size2.Width, size1.Height + size2.Height);
public static Size operator +(in Size size1, in Size size2) => new(size1.Width + size2.Width, size1.Height + size2.Height);
/// <summary>
/// Returns a new <see cref="Rectangle"/> created from the provided <see cref="Point"/> and <see cref="Size"/>.
@ -139,7 +140,7 @@ namespace RGB.NET.Core
/// <param name="size">The <see cref="Size"/> of the rectangle.</param>
/// <param name="point">The <see cref="Point"/> of the rectangle.</param>
/// <returns>The rectangle created from the provided <see cref="Point"/> and <see cref="Size"/>.</returns>
public static Rectangle operator +(Size size, Point point) => new(point, size);
public static Rectangle operator +(in Size size, in Point point) => new(point, size);
/// <summary>
/// Returns a new <see cref="Size"/> representing the subtraction of the two provided <see cref="Size"/>.
@ -147,7 +148,7 @@ namespace RGB.NET.Core
/// <param name="size1">The first <see cref="Size"/>.</param>
/// <param name="size2">The second <see cref="Size"/>.</param>
/// <returns>A new <see cref="Size"/> representing the subtraction of the two provided <see cref="Size"/>.</returns>
public static Size operator -(Size size1, Size size2) => new(size1.Width - size2.Width, size1.Height - size2.Height);
public static Size operator -(in Size size1, in Size size2) => new(size1.Width - size2.Width, size1.Height - size2.Height);
/// <summary>
/// Returns a new <see cref="Size"/> representing the multiplication of the two provided <see cref="Size"/>.
@ -155,7 +156,7 @@ namespace RGB.NET.Core
/// <param name="size1">The first <see cref="Size"/>.</param>
/// <param name="size2">The second <see cref="Size"/>.</param>
/// <returns>A new <see cref="Size"/> representing the multiplication of the two provided <see cref="Size"/>.</returns>
public static Size operator *(Size size1, Size size2) => new(size1.Width * size2.Width, size1.Height * size2.Height);
public static Size operator *(in Size size1, in Size size2) => new(size1.Width * size2.Width, size1.Height * size2.Height);
/// <summary>
/// Returns a new <see cref="Size"/> representing the multiplication of the <see cref="Size"/> and the provided factor.
@ -163,7 +164,7 @@ namespace RGB.NET.Core
/// <param name="size">The <see cref="Size"/>.</param>
/// <param name="factor">The factor by which the <see cref="Size"/> should be multiplied.</param>
/// <returns>A new <see cref="Size"/> representing the multiplication of the <see cref="Size"/> and the provided factor.</returns>
public static Size operator *(Size size, double factor) => new(size.Width * factor, size.Height * factor);
public static Size operator *(in Size size, float factor) => new(size.Width * factor, size.Height * factor);
/// <summary>
/// Returns a new <see cref="Size"/> representing the division of the two provided <see cref="Size"/>.
@ -171,7 +172,7 @@ namespace RGB.NET.Core
/// <param name="size1">The first <see cref="Size"/>.</param>
/// <param name="size2">The second <see cref="Size"/>.</param>
/// <returns>A new <see cref="Size"/> representing the division of the two provided <see cref="Size"/>.</returns>
public static Size operator /(Size size1, Size size2)
public static Size operator /(in Size size1, in Size size2)
=> size2.Width.EqualsInTolerance(0) || size2.Height.EqualsInTolerance(0)
? Invalid : new Size(size1.Width / size2.Width, size1.Height / size2.Height);
@ -181,7 +182,7 @@ namespace RGB.NET.Core
/// <param name="size">The <see cref="Size"/>.</param>
/// <param name="factor">The factor by which the <see cref="Size"/> should be divided.</param>
/// <returns>A new <see cref="Size"/> representing the division of the <see cref="Size"/> and the provided factor.</returns>
public static Size operator /(Size size, double factor) => factor.EqualsInTolerance(0) ? Invalid : new Size(size.Width / factor, size.Height / factor);
public static Size operator /(in Size size, float factor) => factor.EqualsInTolerance(0) ? Invalid : new Size(size.Width / factor, size.Height / factor);
/// <summary>
/// Returns a new <see cref="Size"/> representing the multiplication of the <see cref="Size"/> and the given <see cref="Scale"/>.
@ -189,7 +190,7 @@ namespace RGB.NET.Core
/// <param name="size">The <see cref="Size"/> to scale.</param>
/// <param name="scale">The scaling factor.</param>
/// <returns>A new <see cref="Size"/> representing the multiplication of the <see cref="Size"/> and the given <see cref="Scale"/>.</returns>
public static Size operator *(Size size, Scale scale) => new(size.Width * scale.Horizontal, size.Height * scale.Vertical);
public static Size operator *(in Size size, in Scale scale) => new(size.Width * scale.Horizontal, size.Height * scale.Vertical);
#endregion
}

View File

@ -17,6 +17,10 @@
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=leds/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=mvvm/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=positioning/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=rendering/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=rendering_005Cbrushes/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=rendering_005Ctextures/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=rendering_005Ctextures_005Csampler/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=surfaces/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=update/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=update_005Cdevices/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@ -14,20 +14,15 @@ namespace RGB.NET.Core
/// <summary>
/// Represents a RGB-surface containing multiple devices.
/// </summary>
public class RGBSurface : AbstractBindable, IDisposable
public sealed class RGBSurface : AbstractBindable, IDisposable
{
#region Properties & Fields
private Stopwatch _deltaTimeCounter;
private IList<IRGBDevice> _devices = new List<IRGBDevice>();
private IList<IUpdateTrigger> _updateTriggers = new List<IUpdateTrigger>();
// ReSharper disable InconsistentNaming
private readonly LinkedList<ILedGroup> _ledGroups = new();
// ReSharper restore InconsistentNaming
private readonly IList<IRGBDevice> _devices = new List<IRGBDevice>();
private readonly IList<IUpdateTrigger> _updateTriggers = new List<IUpdateTrigger>();
private readonly List<ILedGroup> _ledGroups = new List<ILedGroup>();
/// <summary>
/// Gets a readonly list containing all loaded <see cref="IRGBDevice"/>.
@ -160,7 +155,7 @@ namespace RGB.NET.Core
lock (_ledGroups)
{
// Render brushes
foreach (ILedGroup ledGroup in _ledGroups.OrderBy(x => x.ZIndex))
foreach (ILedGroup ledGroup in _ledGroups)
try { Render(ledGroup); }
catch (Exception ex) { OnException(ex); }
}
@ -201,33 +196,32 @@ namespace RGB.NET.Core
/// Renders a ledgroup.
/// </summary>
/// <param name="ledGroup">The led group to render.</param>
/// <exception cref="ArgumentException">Thrown if the <see cref="IBrush.CalculationMode"/> of the Brush is not valid.</exception>
private void Render(ILedGroup ledGroup)
{
IList<Led> leds = ledGroup.GetLeds().ToList();
IList<Led> leds = ledGroup.ToList();
IBrush? brush = ledGroup.Brush;
if ((brush == null) || !brush.IsEnabled) return;
switch (brush.BrushCalculationMode)
IEnumerable<(RenderTarget renderTarget, Color color)> render;
switch (brush.CalculationMode)
{
case BrushCalculationMode.Relative:
case RenderMode.Relative:
Rectangle brushRectangle = new(leds.Select(led => led.AbsoluteBoundary));
Point offset = new(-brushRectangle.Location.X, -brushRectangle.Location.Y);
brushRectangle = brushRectangle.SetLocation(new Point(0, 0));
brush.PerformRender(brushRectangle, leds.Select(led => new BrushRenderTarget(led, led.AbsoluteBoundary.Translate(offset))));
render = brush.Render(brushRectangle, leds.Select(led => new RenderTarget(led, led.AbsoluteBoundary.Translate(offset))));
break;
case BrushCalculationMode.Absolute:
brush.PerformRender(Boundary, leds.Select(led => new BrushRenderTarget(led, led.AbsoluteBoundary)));
case RenderMode.Absolute:
render = brush.Render(Boundary, leds.Select(led => new RenderTarget(led, led.AbsoluteBoundary)));
break;
default:
throw new ArgumentException();
throw new ArgumentException($"The CalculationMode '{brush.CalculationMode}' is not valid.");
}
//brush.UpdateEffects();
brush.PerformFinalize();
foreach (KeyValuePair<BrushRenderTarget, Color> renders in brush.RenderedTargets)
renders.Key.Led.Color = renders.Value;
foreach ((RenderTarget renderTarget, Color c) in render)
renderTarget.Led.Color = c;
}
/// <summary>
@ -235,14 +229,16 @@ namespace RGB.NET.Core
/// </summary>
/// <param name="ledGroup">The <see cref="ILedGroup"/> to attach.</param>
/// <returns><c>true</c> if the <see cref="ILedGroup"/> could be attached; otherwise, <c>false</c>.</returns>
public bool AttachLedGroup(ILedGroup ledGroup)
public bool Attach(ILedGroup ledGroup)
{
lock (_ledGroups)
{
if (_ledGroups.Contains(ledGroup)) return false;
if (ledGroup.Surface != null) return false;
_ledGroups.AddLast(ledGroup);
ledGroup.OnAttach(this);
ledGroup.Surface = this;
_ledGroups.Add(ledGroup);
_ledGroups.Sort((group1, group2) => group1.ZIndex.CompareTo(group2.ZIndex));
ledGroup.OnAttach();
return true;
}
@ -253,29 +249,18 @@ namespace RGB.NET.Core
/// </summary>
/// <param name="ledGroup">The <see cref="ILedGroup"/> to detached.</param>
/// <returns><c>true</c> if the <see cref="ILedGroup"/> could be detached; otherwise, <c>false</c>.</returns>
public bool DetachLedGroup(ILedGroup ledGroup)
public bool Detach(ILedGroup ledGroup)
{
lock (_ledGroups)
{
LinkedListNode<ILedGroup>? node = _ledGroups.Find(ledGroup);
if (node == null) return false;
_ledGroups.Remove(node);
node.Value.OnDetach(this);
if (!_ledGroups.Remove(ledGroup)) return false;
ledGroup.OnDetach();
ledGroup.Surface = null;
return true;
}
}
public void Attach(IEnumerable<IRGBDevice> devices)
{
lock (_devices)
{
foreach (IRGBDevice device in devices)
Attach(device);
}
}
public void Attach(IRGBDevice device)
{
lock (_devices)
@ -290,20 +275,11 @@ namespace RGB.NET.Core
}
}
public void Detach(IEnumerable<IRGBDevice> devices)
{
lock (_devices)
{
foreach (IRGBDevice device in devices)
Detach(device);
}
}
public void Detach(IRGBDevice device)
{
lock (_devices)
{
if (!_devices.Contains(device)) throw new RGBSurfaceException($"The device '{device.DeviceInfo.Manufacturer} {device.DeviceInfo.Model}' isn't not attached to this surface.");
if (!_devices.Contains(device)) throw new RGBSurfaceException($"The device '{device.DeviceInfo.Manufacturer} {device.DeviceInfo.Model}' is not attached to this surface.");
device.BoundaryChanged -= DeviceOnBoundaryChanged;
device.Surface = null;
@ -314,19 +290,6 @@ namespace RGB.NET.Core
}
}
/// <summary>
/// Automatically aligns all devices to prevent overlaps.
/// </summary>
public void AlignDevices()
{
double posX = 0;
foreach (IRGBDevice device in Devices)
{
device.Location += new Point(posX, 0);
posX += device.ActualSize.Width + 1;
}
}
// ReSharper restore UnusedMember.Global
private void DeviceOnBoundaryChanged(object? sender, EventArgs args)
@ -348,29 +311,6 @@ namespace RGB.NET.Core
}
}
/// <summary>
/// Gets all devices of a specific type.
/// </summary>
/// <typeparam name="T">The type of devices to get.</typeparam>
/// <returns>A list of devices with the specified type.</returns>
public IList<T> GetDevices<T>()
where T : class
{
lock (_devices)
return new ReadOnlyCollection<T>(_devices.Where(x => x is T).Cast<T>().ToList());
}
/// <summary>
/// Gets all devices of the specified <see cref="RGBDeviceType"/>.
/// </summary>
/// <param name="deviceType">The <see cref="RGBDeviceType"/> of the devices to get.</param>
/// <returns>a list of devices matching the specified <see cref="RGBDeviceType"/>.</returns>
public IList<IRGBDevice> GetDevices(RGBDeviceType deviceType)
{
lock (_devices)
return new ReadOnlyCollection<IRGBDevice>(_devices.Where(d => deviceType.HasFlag(d.DeviceInfo.DeviceType)).ToList());
}
/// <summary>
/// Registers the provided <see cref="IUpdateTrigger"/>.
/// </summary>

View File

@ -3,7 +3,6 @@
// ReSharper disable VirtualMemberNeverOverridden.Global
using System.Collections.Generic;
using System.Linq;
namespace RGB.NET.Core
{
@ -20,22 +19,13 @@ namespace RGB.NET.Core
public bool IsEnabled { get; set; } = true;
/// <inheritdoc />
public BrushCalculationMode BrushCalculationMode { get; set; } = BrushCalculationMode.Relative;
public RenderMode CalculationMode { get; set; } = RenderMode.Relative;
/// <inheritdoc />
public double Brightness { get; set; }
public float Brightness { get; set; }
/// <inheritdoc />
public double Opacity { get; set; }
/// <inheritdoc />
public IList<IColorCorrection> ColorCorrections { get; } = new List<IColorCorrection>();
/// <inheritdoc />
public Rectangle RenderedRectangle { get; protected set; }
/// <inheritdoc />
public Dictionary<BrushRenderTarget, Color> RenderedTargets { get; } = new();
public float Opacity { get; set; }
#endregion
@ -46,7 +36,7 @@ namespace RGB.NET.Core
/// </summary>
/// <param name="brightness">The overall percentage brightness of the brush. (default: 1.0)</param>
/// <param name="opacity">The overall percentage opacity of the brush. (default: 1.0)</param>
protected AbstractBrush(double brightness = 1, double opacity = 1)
protected AbstractBrush(float brightness = 1, float opacity = 1)
{
this.Brightness = brightness;
this.Opacity = opacity;
@ -56,17 +46,14 @@ namespace RGB.NET.Core
#region Methods
/// <inheritdoc />
public virtual void PerformRender(Rectangle rectangle, IEnumerable<BrushRenderTarget> renderTargets)
public virtual IEnumerable<(RenderTarget renderTarget, Color color)> Render(Rectangle rectangle, IEnumerable<RenderTarget> renderTargets)
{
RenderedRectangle = rectangle;
RenderedTargets.Clear();
foreach (BrushRenderTarget renderTarget in renderTargets)
foreach (RenderTarget renderTarget in renderTargets)
{
Color color = GetColorAtPoint(rectangle, renderTarget);
color = ApplyDecorators(rectangle, renderTarget, color);
RenderedTargets[renderTarget] = color;
ApplyDecorators(rectangle, renderTarget, ref color);
FinalizeColor(ref color);
yield return (renderTarget, color);
}
}
@ -76,22 +63,14 @@ namespace RGB.NET.Core
/// <param name="rectangle">The rectangle in which the brush should be drawn.</param>
/// <param name="renderTarget">The target (key/point) from which the color should be taken.</param>
/// <param name="color">The <see cref="Color"/> to be modified.</param>
protected virtual Color ApplyDecorators(Rectangle rectangle, BrushRenderTarget renderTarget, Color color)
protected virtual void ApplyDecorators(in Rectangle rectangle, in RenderTarget renderTarget, ref Color color)
{
if (Decorators.Count == 0) return;
lock (Decorators)
foreach (IBrushDecorator decorator in Decorators)
if (decorator.IsEnabled)
color = decorator.ManipulateColor(rectangle, renderTarget, color);
return color;
}
/// <inheritdoc />
public virtual void PerformFinalize()
{
List<BrushRenderTarget> renderTargets = RenderedTargets.Keys.ToList();
foreach (BrushRenderTarget renderTarget in renderTargets)
RenderedTargets[renderTarget] = FinalizeColor(RenderedTargets[renderTarget]);
decorator.ManipulateColor(rectangle, renderTarget, ref color);
}
/// <summary>
@ -100,20 +79,15 @@ namespace RGB.NET.Core
/// <param name="rectangle">The rectangle in which the brush should be drawn.</param>
/// <param name="renderTarget">The target (key/point) from which the color should be taken.</param>
/// <returns>The color at the specified point.</returns>
protected abstract Color GetColorAtPoint(Rectangle rectangle, BrushRenderTarget renderTarget);
protected abstract Color GetColorAtPoint(in Rectangle rectangle, in RenderTarget renderTarget);
/// <summary>
/// Finalizes the color by appliing the overall brightness and opacity.<br/>
/// This method should always be the last call of a <see cref="GetColorAtPoint" /> implementation.
/// </summary>
/// <param name="color">The color to finalize.</param>
/// <returns>The finalized color.</returns>
protected virtual Color FinalizeColor(Color color)
protected virtual void FinalizeColor(ref Color color)
{
if (ColorCorrections.Count > 0)
foreach (IColorCorrection colorCorrection in ColorCorrections)
color = colorCorrection.ApplyTo(color);
// Since we use HSV to calculate there is no way to make a color 'brighter' than 100%
// Be carefull with the naming: Since we use HSV the correct term is 'value' but outside we call it 'brightness'
// THIS IS NOT A HSB CALCULATION!!!
@ -122,8 +96,6 @@ namespace RGB.NET.Core
if (Opacity < 1)
color = color.MultiplyA(Opacity.Clamp(0, 1));
return color;
}
#endregion

View File

@ -0,0 +1,41 @@
// ReSharper disable UnusedMemberInSuper.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable ReturnTypeCanBeEnumerable.Global
using System.Collections.Generic;
namespace RGB.NET.Core
{
/// <summary>
/// Represents a basic brush.
/// </summary>
public interface IBrush : IDecoratable<IBrushDecorator>
{
/// <summary>
/// Gets or sets if the <see cref="IBrush"/> is enabled and will be drawn on an update.
/// </summary>
bool IsEnabled { get; set; }
/// <summary>
/// Gets or sets the calculation mode used for the rectangle/points used for color-selection in brushes.
/// </summary>
RenderMode CalculationMode { get; set; }
/// <summary>
/// Gets or sets the overall percentage brightness of the <see cref="IBrush"/>.
/// </summary>
float Brightness { get; set; }
/// <summary>
/// Gets or sets the overall percentage opacity of the <see cref="IBrush"/>.
/// </summary>
float Opacity { get; set; }
/// <summary>
/// Performs the render pass of the <see cref="IBrush"/> and calculates the raw <see cref="Color"/> for all requested <see cref="RenderTarget"/>.
/// </summary>
/// <param name="rectangle">The <see cref="Rectangle"/> in which the brush should be drawn.</param>
/// <param name="renderTargets">The <see cref="RenderTarget"/> (keys/points) of which the color should be calculated.</param>
IEnumerable<(RenderTarget renderTarget, Color color)> Render(Rectangle rectangle, IEnumerable<RenderTarget> renderTargets);
}
}

View File

@ -1,9 +1,7 @@
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
using RGB.NET.Core;
namespace RGB.NET.Brushes
namespace RGB.NET.Core
{
/// <inheritdoc />
/// <summary>
@ -29,9 +27,9 @@ namespace RGB.NET.Brushes
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.SolidColorBrush" /> class.
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.SolidColorBrush" /> class.
/// </summary>
/// <param name="color">The <see cref="P:RGB.NET.Brushes.SolidColorBrush.Color" /> drawn by this <see cref="T:RGB.NET.Brushes.SolidColorBrush" />.</param>
/// <param name="color">The <see cref="P:RGB.NET.Core.SolidColorBrush.Color" /> drawn by this <see cref="T:RGB.NET.Core.SolidColorBrush" />.</param>
public SolidColorBrush(Color color)
{
this.Color = color;
@ -42,7 +40,7 @@ namespace RGB.NET.Brushes
#region Methods
/// <inheritdoc />
protected override Color GetColorAtPoint(Rectangle rectangle, BrushRenderTarget renderTarget) => Color;
protected override Color GetColorAtPoint(in Rectangle rectangle, in RenderTarget renderTarget) => Color;
#endregion

View File

@ -0,0 +1,35 @@
namespace RGB.NET.Core
{
public class TextureBrush : AbstractBrush
{
#region Properties & Fields
private ITexture _texture = ITexture.Empty;
public ITexture Texture
{
get => _texture;
set => SetProperty(ref _texture, value);
}
#endregion
#region Constructors
public TextureBrush(ITexture texture)
{
this.Texture = texture;
}
#endregion
#region Methods
protected override Color GetColorAtPoint(in Rectangle rectangle, in RenderTarget renderTarget)
{
Rectangle normalizedRect = renderTarget.Rectangle / rectangle;
return Texture[normalizedRect];
}
#endregion
}
}

View File

@ -5,7 +5,7 @@ namespace RGB.NET.Core
/// <summary>
/// Contains a list of all brush calculation modes.
/// </summary>
public enum BrushCalculationMode
public enum RenderMode
{
/// <summary>
/// The calculation <see cref="Rectangle"/> for <see cref="IBrush"/> will be the rectangle around the <see cref="ILedGroup"/> the <see cref="IBrush"/> is applied to.
@ -13,7 +13,7 @@ namespace RGB.NET.Core
Relative,
/// <summary>
/// The calculation <see cref="Rectangle"/> for <see cref="IBrush"/> will always be the rectangle completly containing all affected <see cref="IRGBDevice"/>.
/// The calculation <see cref="Rectangle"/> for <see cref="IBrush"/> will always be the whole <see cref="RGBSurface"/>.
/// </summary>
Absolute
}

View File

@ -6,7 +6,7 @@ namespace RGB.NET.Core
/// <summary>
/// Represents a single target of a brush render.
/// </summary>
public class BrushRenderTarget
public readonly struct RenderTarget
{
#region Properties & Fields
@ -23,23 +23,21 @@ namespace RGB.NET.Core
/// <summary>
/// Gets the <see cref="Core.Point"/> representing the position to render the target-<see cref="Core.Led"/>.
/// </summary>
public Point Point { get; }
public Point Point => Rectangle.Center;
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="BrushRenderTarget"/> class.
/// Initializes a new instance of the <see cref="RenderTarget"/> class.
/// </summary>
/// <param name="led">The target-<see cref="Core.Led"/>.</param>
/// <param name="rectangle">The <see cref="Core.Rectangle"/> representing the area to render the target-<see cref="Core.Led"/>.</param>
public BrushRenderTarget(Led led, Rectangle rectangle)
public RenderTarget(Led led, Rectangle rectangle)
{
this.Led = led;
this.Rectangle = rectangle;
this.Point = rectangle.Center;
}
#endregion

View File

@ -0,0 +1,13 @@
namespace RGB.NET.Core
{
internal class EmptyTexture : ITexture
{
#region Properties & Fields
public Size Size { get; } = new(0, 0);
public Color this[in Point point] => Color.Transparent;
public Color this[in Rectangle rectangle] => Color.Transparent;
#endregion
}
}

View File

@ -0,0 +1,12 @@
namespace RGB.NET.Core
{
public interface ITexture
{
static ITexture Empty => new EmptyTexture();
Size Size { get; }
Color this[in Point point] { get; }
Color this[in Rectangle rectangle] { get; }
}
}

View File

@ -0,0 +1,149 @@
using System;
using System.Buffers;
using System.Runtime.CompilerServices;
namespace RGB.NET.Core
{
public abstract class PixelTexture<T> : ITexture
where T : unmanaged
{
#region Constants
private const int STACK_ALLOC_LIMIT = 1024;
#endregion
#region Properties & Fields
private readonly int _dataPerPixel;
private readonly int _stride;
public ISampler<T> Sampler { get; set; }
public Size Size { get; }
protected abstract ReadOnlySpan<T> Data { get; }
public virtual Color this[in Point point]
{
get
{
if (Data.Length == 0) return Color.Transparent;
int x = (int)MathF.Round(Size.Width * point.X.Clamp(0, 1));
int y = (int)MathF.Round(Size.Height * point.Y.Clamp(0, 1));
return GetColor(GetPixelData(x, y));
}
}
public virtual Color this[in Rectangle rectangle]
{
get
{
if (Data.Length == 0) return Color.Transparent;
int x = (int)MathF.Round(Size.Width * rectangle.Location.X.Clamp(0, 1));
int y = (int)MathF.Round(Size.Height * rectangle.Location.Y.Clamp(0, 1));
int width = (int)MathF.Round(Size.Width * rectangle.Size.Width.Clamp(0, 1));
int height = (int)MathF.Round(Size.Height * rectangle.Size.Height.Clamp(0, 1));
if ((width == 0) || (height == 0)) return Color.Transparent;
if ((width == 1) && (height == 1)) return GetColor(GetPixelData(x, y));
int bufferSize = width * height * _dataPerPixel;
if (bufferSize <= STACK_ALLOC_LIMIT)
{
Span<T> buffer = stackalloc T[bufferSize];
GetRegionData(x, y, width, height, buffer);
Span<T> pixelData = stackalloc T[_dataPerPixel];
Sampler.SampleColor(new SamplerInfo<T>(width, height, buffer), pixelData);
return GetColor(pixelData);
}
else
{
T[] rent = ArrayPool<T>.Shared.Rent(bufferSize);
Span<T> buffer = new Span<T>(rent).Slice(0, bufferSize);
GetRegionData(x, y, width, height, buffer);
Span<T> pixelData = stackalloc T[_dataPerPixel];
Sampler.SampleColor(new SamplerInfo<T>(width, height, buffer), pixelData);
ArrayPool<T>.Shared.Return(rent);
return GetColor(pixelData);
}
}
}
#endregion
#region Constructors
public PixelTexture(int with, int height, int dataPerPixel, ISampler<T> sampler, int stride = -1)
{
this._stride = stride == -1 ? with : stride;
this._dataPerPixel = dataPerPixel;
this.Sampler = sampler;
Size = new Size(with, height);
}
#endregion
#region Methods
protected abstract Color GetColor(in ReadOnlySpan<T> pixel);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected virtual ReadOnlySpan<T> GetPixelData(int x, int y) => Data.Slice((y * _stride) + x, _dataPerPixel);
protected virtual void GetRegionData(int x, int y, int width, int height, in Span<T> buffer)
{
int dataWidth = width * _dataPerPixel;
ReadOnlySpan<T> data = Data;
for (int i = 0; i < height; i++)
{
ReadOnlySpan<T> dataSlice = data.Slice((((y + i) * _stride) + x) * _dataPerPixel, dataWidth);
Span<T> destination = buffer.Slice(i * dataWidth, dataWidth);
dataSlice.CopyTo(destination);
}
}
#endregion
}
public sealed class PixelTexture : PixelTexture<Color>
{
#region Properties & Fields
private readonly Color[] _data;
protected override ReadOnlySpan<Color> Data => _data;
#endregion
#region Constructors
public PixelTexture(int with, int height, Color[] data)
: this(with, height, data, new AverageColorSampler())
{ }
public PixelTexture(int with, int height, Color[] data, ISampler<Color> sampler)
: base(with, height, 1, sampler)
{
this._data = data;
if (Data.Length != (with * height)) throw new ArgumentException($"Data-Length {Data.Length} differs from the given size {with}x{height} ({with * height}).");
}
#endregion
#region Methods
protected override Color GetColor(in ReadOnlySpan<Color> pixel) => pixel[0];
#endregion
}
}

View File

@ -0,0 +1,28 @@
using System;
namespace RGB.NET.Core
{
public class AverageColorSampler : ISampler<Color>
{
#region Methods
public void SampleColor(in SamplerInfo<Color> info, in Span<Color> pixelData)
{
int count = info.Width * info.Height;
if (count == 0) return;
float a = 0, r = 0, g = 0, b = 0;
foreach (Color color in info.Data)
{
a += color.A;
r += color.R;
g += color.G;
b += color.B;
}
pixelData[0] = new Color(a / count, r / count, g / count, b / count);
}
#endregion
}
}

View File

@ -0,0 +1,9 @@
using System;
namespace RGB.NET.Core
{
public interface ISampler<T>
{
void SampleColor(in SamplerInfo<T> info, in Span<T> pixelData);
}
}

View File

@ -0,0 +1,26 @@
using System;
namespace RGB.NET.Core
{
public readonly ref struct SamplerInfo<T>
{
#region Properties & Fields
public int Width { get; }
public int Height { get; }
public ReadOnlySpan<T> Data { get; }
#endregion
#region Constructors
public SamplerInfo(int width, int height, ReadOnlySpan<T> data)
{
this.Width = width;
this.Height = height;
this.Data = data;
}
#endregion
}
}

View File

@ -30,6 +30,9 @@ namespace RGB.NET.Core
/// <param name="updateData">Optional custom-data passed to the subscribers of the <see cref="Update"/>.event.</param>
protected virtual void OnUpdate(CustomUpdateData? updateData = null) => Update?.Invoke(this, updateData ?? new CustomUpdateData());
/// <inheritdoc />
public abstract void Start();
/// <inheritdoc />
public abstract void Dispose();

View File

@ -86,7 +86,7 @@ namespace RGB.NET.Core
/// <summary>
/// Starts the trigger.
/// </summary>
public void Start()
public override void Start()
{
if (IsRunning) return;

View File

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
namespace RGB.NET.Core
{
public interface IUpdateQueue<TIdentifier, TData> : IDisposable
where TIdentifier : notnull
{
/// <summary>
/// Sets or merges the provided data set in the current dataset and notifies the trigger that there is new data available.
/// </summary>
/// <param name="dataSet">The set of data.</param>
// ReSharper disable once MemberCanBeProtected.Global
void SetData(IEnumerable<(TIdentifier, TData)> dataSet);
/// <summary>
/// Resets the current data set.
/// </summary>
void Reset();
}
public interface IUpdateQueue : IUpdateQueue<object, Color>
{ }
}

View File

@ -1,4 +1,5 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
@ -9,14 +10,14 @@ namespace RGB.NET.Core
/// </summary>
/// <typeparam name="TIdentifier">The type of the key used to identify some data.</typeparam>
/// <typeparam name="TData">The type of the data.</typeparam>
public abstract class UpdateQueue<TIdentifier, TData> : IDisposable
public abstract class UpdateQueue<TIdentifier, TData> : IUpdateQueue<TIdentifier, TData>
where TIdentifier : notnull
{
#region Properties & Fields
private readonly object _dataLock = new();
private readonly IDeviceUpdateTrigger _updateTrigger;
private Dictionary<TIdentifier, TData>? _currentDataSet;
private readonly Dictionary<TIdentifier, TData> _currentDataSet = new();
#endregion
@ -45,17 +46,25 @@ namespace RGB.NET.Core
/// <param name="customData"><see cref="CustomUpdateData"/> provided by the trigger.</param>
protected virtual void OnUpdate(object? sender, CustomUpdateData customData)
{
Dictionary<TIdentifier, TData> dataSet;
(TIdentifier, TData)[] dataSet;
Span<(TIdentifier, TData)> data;
lock (_dataLock)
{
if (_currentDataSet == null) return;
if (_currentDataSet.Count == 0) return;
dataSet = _currentDataSet;
_currentDataSet = null;
dataSet = ArrayPool<(TIdentifier, TData)>.Shared.Rent(_currentDataSet.Count);
data = new Span<(TIdentifier, TData)>(dataSet).Slice(0, _currentDataSet.Count);
int i = 0;
foreach ((TIdentifier key, TData value) in _currentDataSet)
data[i++] = (key, value);
_currentDataSet.Clear();
}
if (dataSet.Count != 0)
Update(dataSet);
Update(data);
ArrayPool<(TIdentifier, TData)>.Shared.Return(dataSet);
}
/// <summary>
@ -69,26 +78,22 @@ namespace RGB.NET.Core
/// Performs the update this queue is responsible for.
/// </summary>
/// <param name="dataSet">The set of data that needs to be updated.</param>
protected abstract void Update(Dictionary<TIdentifier, TData> dataSet);
protected abstract void Update(in ReadOnlySpan<(TIdentifier key, TData color)> dataSet);
/// <summary>
/// Sets or merges the provided data set in the current dataset and notifies the trigger that there is new data available.
/// </summary>
/// <param name="dataSet">The set of data.</param>
// ReSharper disable once MemberCanBeProtected.Global
public virtual void SetData(Dictionary<TIdentifier, TData> dataSet)
public virtual void SetData(IEnumerable<(TIdentifier, TData)> dataSet)
{
if (dataSet.Count == 0) return;
IList<(TIdentifier, TData)> data = dataSet.ToList();
if (data.Count == 0) return;
lock (_dataLock)
{
if (_currentDataSet == null)
_currentDataSet = dataSet;
else
{
foreach ((TIdentifier key, TData value) in dataSet)
_currentDataSet[key] = value;
}
foreach ((TIdentifier key, TData value) in data)
_currentDataSet[key] = value;
}
_updateTrigger.TriggerHasData();
@ -100,7 +105,7 @@ namespace RGB.NET.Core
public virtual void Reset()
{
lock (_dataLock)
_currentDataSet = null;
_currentDataSet.Clear();
}
/// <inheritdoc />
@ -118,7 +123,7 @@ namespace RGB.NET.Core
/// <summary>
/// Represents a generic <see cref="UpdateQueue{TIdentifier,TData}"/> using an object as the key and a color as the value.
/// </summary>
public abstract class UpdateQueue : UpdateQueue<object, Color>
public abstract class UpdateQueue : UpdateQueue<object, Color>, IUpdateQueue
{
#region Constructors
@ -128,15 +133,5 @@ namespace RGB.NET.Core
{ }
#endregion
#region Methods
/// <summary>
/// Calls <see cref="UpdateQueue{TIdentifier,TData}.SetData"/> for a data set created out of the provided list of <see cref="Led"/>.
/// </summary>
/// <param name="leds"></param>
public void SetData(IEnumerable<Led> leds) => SetData(leds.ToDictionary(x => x.CustomData ?? x.Id, x => x.Color));
#endregion
}
}

View File

@ -16,5 +16,7 @@ namespace RGB.NET.Core
/// Occurs when the trigger wants to cause an update.
/// </summary>
event EventHandler<CustomUpdateData>? Update;
void Start();
}
}

View File

@ -0,0 +1,94 @@
// ReSharper disable MemberCanBePrivate.Global
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace RGB.NET.Core
{
/// <inheritdoc />
/// <summary>
/// Represents an <see cref="T:RGB.NET.Core.IUpdateTrigger" />
/// </summary>
public sealed class ManualUpdateTrigger : AbstractUpdateTrigger
{
#region Properties & Fields
private AutoResetEvent _mutex = new(false);
private Task? UpdateTask { get; set; }
private CancellationTokenSource? UpdateTokenSource { get; set; }
private CancellationToken UpdateToken { get; set; }
/// <summary>
/// Gets the time it took the last update-loop cycle to run.
/// </summary>
public double LastUpdateTime { get; private set; }
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="ManualUpdateTrigger"/> class.
/// </summary>
/// <param name="autostart">A value indicating if the trigger should automatically <see cref="Start"/> right after construction.</param>
public ManualUpdateTrigger()
{
Start();
}
#endregion
#region Methods
/// <summary>
/// Starts the trigger if needed, causing it to performing updates.
/// </summary>
public override void Start()
{
if (UpdateTask == null)
{
UpdateTokenSource?.Dispose();
UpdateTokenSource = new CancellationTokenSource();
UpdateTask = Task.Factory.StartNew(UpdateLoop, (UpdateToken = UpdateTokenSource.Token), TaskCreationOptions.LongRunning, TaskScheduler.Default);
}
}
/// <summary>
/// Stops the trigger if running, causing it to stop performing updates.
/// </summary>
private void Stop()
{
if (UpdateTask != null)
{
UpdateTokenSource?.Cancel();
// ReSharper disable once MethodSupportsCancellation
UpdateTask.Wait();
UpdateTask.Dispose();
UpdateTask = null;
}
}
public void TriggerUpdate() => _mutex.Set();
private void UpdateLoop()
{
OnStartup();
while (!UpdateToken.IsCancellationRequested)
{
if (_mutex.WaitOne(100))
{
long preUpdateTicks = Stopwatch.GetTimestamp();
OnUpdate();
LastUpdateTime = ((Stopwatch.GetTimestamp() - preUpdateTicks) / 10000.0);
}
}
}
/// <inheritdoc />
public override void Dispose() => Stop();
#endregion
}
}

View File

@ -56,7 +56,7 @@ namespace RGB.NET.Core
/// <summary>
/// Starts the trigger if needed, causing it to performing updates.
/// </summary>
public void Start()
public override void Start()
{
lock (_lock)
{

View File

@ -1,57 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net5.0</TargetFrameworks>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<Authors>Darth Affe</Authors>
<Company>Wyrez</Company>
<Language>en-US</Language>
<NeutralLanguage>en-US</NeutralLanguage>
<Title>RGB.NET.Decorators</Title>
<AssemblyName>RGB.NET.Decorators</AssemblyName>
<AssemblyTitle>RGB.NET.Decorators</AssemblyTitle>
<PackageId>RGB.NET.Decorators</PackageId>
<RootNamespace>RGB.NET.Decorators</RootNamespace>
<Description>Decorators-Presets of RGB.NET</Description>
<Summary>Decorators-Presets of RGB.NET, a C# (.NET) library for accessing various RGB-peripherals</Summary>
<Copyright>Copyright © Darth Affe 2020</Copyright>
<PackageCopyright>Copyright © Darth Affe 2020</PackageCopyright>
<PackageIconUrl>http://lib.arge.be/icon.png</PackageIconUrl>
<PackageProjectUrl>https://github.com/DarthAffe/RGB.NET</PackageProjectUrl>
<PackageLicenseUrl>https://raw.githubusercontent.com/DarthAffe/RGB.NET/master/LICENSE</PackageLicenseUrl>
<RepositoryType>Github</RepositoryType>
<RepositoryUrl>https://github.com/DarthAffe/RGB.NET</RepositoryUrl>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<PackageReleaseNotes></PackageReleaseNotes>
<Version>0.0.1</Version>
<AssemblyVersion>0.0.1</AssemblyVersion>
<FileVersion>0.0.1</FileVersion>
<OutputPath>..\bin\</OutputPath>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<IncludeSource>True</IncludeSource>
<IncludeSymbols>True</IncludeSymbols>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<DefineConstants>$(DefineConstants);TRACE;DEBUG</DefineConstants>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<NoWarn>$(NoWarn);CS1591;CS1572;CS1573</NoWarn>
<DefineConstants>$(DefineConstants);RELEASE</DefineConstants>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\RGB.NET.Brushes\RGB.NET.Brushes.csproj" />
<ProjectReference Include="..\RGB.NET.Core\RGB.NET.Core.csproj" />
</ItemGroup>
</Project>

View File

@ -3,8 +3,6 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using AuraServiceLib;
using RGB.NET.Core;
@ -14,7 +12,7 @@ namespace RGB.NET.Devices.Asus
/// <summary>
/// Represents a device provider responsible for Cooler Master devices.
/// </summary>
public class AsusDeviceProvider : IRGBDeviceProvider
public class AsusDeviceProvider : AbstractRGBDeviceProvider
{
#region Properties & Fields
@ -24,20 +22,6 @@ namespace RGB.NET.Devices.Asus
/// </summary>
public static AsusDeviceProvider Instance => _instance ?? new AsusDeviceProvider();
/// <inheritdoc />
/// <summary>
/// Indicates if the SDK is initialized and ready to use.
/// </summary>
public bool IsInitialized { get; private set; }
/// <inheritdoc />
public IEnumerable<IRGBDevice> Devices { get; private set; } = Enumerable.Empty<IRGBDevice>();
/// <summary>
/// The <see cref="DeviceUpdateTrigger"/> used to trigger the updates for asus devices.
/// </summary>
public DeviceUpdateTrigger UpdateTrigger { get; }
private IAuraSdk2? _sdk;
#endregion
@ -52,107 +36,44 @@ namespace RGB.NET.Devices.Asus
{
if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(AsusDeviceProvider)}");
_instance = this;
UpdateTrigger = new DeviceUpdateTrigger();
}
#endregion
#region Methods
/// <inheritdoc />
public bool Initialize(RGBDeviceType loadFilter = RGBDeviceType.All, bool throwExceptions = false)
protected override void InitializeSDK()
{
IsInitialized = false;
_sdk = (IAuraSdk2)new AuraSdk();
_sdk.SwitchMode();
}
try
protected override IEnumerable<IRGBDevice> LoadDevices()
{
if (_sdk == null) yield break;
foreach (IAuraSyncDevice device in _sdk.Enumerate(0))
{
UpdateTrigger.Stop();
// ReSharper disable once SuspiciousTypeConversion.Global
_sdk = (IAuraSdk2)new AuraSdk();
_sdk.SwitchMode();
IList<IRGBDevice> devices = new List<IRGBDevice>();
foreach (IAuraSyncDevice device in _sdk.Enumerate(0))
yield return (AsusDeviceType)device.Type switch
{
try
{
IAsusRGBDevice rgbDevice;
switch ((AsusDeviceType)device.Type)
{
case AsusDeviceType.MB_RGB:
rgbDevice = new AsusMainboardRGBDevice(new AsusRGBDeviceInfo(RGBDeviceType.Mainboard, device, WMIHelper.GetMainboardInfo()?.model ?? device.Name));
break;
case AsusDeviceType.MB_ADDRESABLE:
rgbDevice = new AsusUnspecifiedRGBDevice(new AsusRGBDeviceInfo(RGBDeviceType.LedStripe, device), LedId.LedStripe1);
break;
case AsusDeviceType.VGA_RGB:
rgbDevice = new AsusGraphicsCardRGBDevice(new AsusRGBDeviceInfo(RGBDeviceType.GraphicsCard, device));
break;
case AsusDeviceType.HEADSET_RGB:
rgbDevice = new AsusHeadsetRGBDevice(new AsusRGBDeviceInfo(RGBDeviceType.Headset, device));
break;
case AsusDeviceType.DRAM_RGB:
rgbDevice = new AsusDramRGBDevice(new AsusRGBDeviceInfo(RGBDeviceType.DRAM, device));
break;
case AsusDeviceType.KEYBOARD_RGB:
case AsusDeviceType.NB_KB_RGB:
case AsusDeviceType.NB_KB_4ZONE_RGB:
rgbDevice = new AsusKeyboardRGBDevice(new AsusKeyboardRGBDeviceInfo(device));
break;
case AsusDeviceType.MOUSE_RGB:
rgbDevice = new AsusMouseRGBDevice(new AsusRGBDeviceInfo(RGBDeviceType.Mouse, device));
break;
default:
rgbDevice = new AsusUnspecifiedRGBDevice(new AsusRGBDeviceInfo(RGBDeviceType.Unknown, device), LedId.Custom1);
break;
}
if (loadFilter.HasFlag(rgbDevice.DeviceInfo.DeviceType))
{
rgbDevice.Initialize(UpdateTrigger);
devices.Add(rgbDevice);
}
}
catch
{
if (throwExceptions)
throw;
}
}
UpdateTrigger.Start();
Devices = new ReadOnlyCollection<IRGBDevice>(devices);
IsInitialized = true;
AsusDeviceType.MB_RGB => new AsusMainboardRGBDevice(new AsusRGBDeviceInfo(RGBDeviceType.Mainboard, device, WMIHelper.GetMainboardInfo()?.model ?? device.Name), GetUpdateTrigger()),
AsusDeviceType.MB_ADDRESABLE => new AsusUnspecifiedRGBDevice(new AsusRGBDeviceInfo(RGBDeviceType.LedStripe, device), LedId.LedStripe1, GetUpdateTrigger()),
AsusDeviceType.VGA_RGB => new AsusGraphicsCardRGBDevice(new AsusRGBDeviceInfo(RGBDeviceType.GraphicsCard, device), GetUpdateTrigger()),
AsusDeviceType.HEADSET_RGB => new AsusHeadsetRGBDevice(new AsusRGBDeviceInfo(RGBDeviceType.Headset, device), GetUpdateTrigger()),
AsusDeviceType.DRAM_RGB => new AsusDramRGBDevice(new AsusRGBDeviceInfo(RGBDeviceType.DRAM, device), GetUpdateTrigger()),
AsusDeviceType.KEYBOARD_RGB => new AsusKeyboardRGBDevice(new AsusKeyboardRGBDeviceInfo(device), GetUpdateTrigger()),
AsusDeviceType.NB_KB_RGB => new AsusKeyboardRGBDevice(new AsusKeyboardRGBDeviceInfo(device), GetUpdateTrigger()),
AsusDeviceType.NB_KB_4ZONE_RGB => new AsusKeyboardRGBDevice(new AsusKeyboardRGBDeviceInfo(device), GetUpdateTrigger()),
AsusDeviceType.MOUSE_RGB => new AsusMouseRGBDevice(new AsusRGBDeviceInfo(RGBDeviceType.Mouse, device), GetUpdateTrigger()),
_ => new AsusUnspecifiedRGBDevice(new AsusRGBDeviceInfo(RGBDeviceType.Unknown, device), LedId.Custom1, GetUpdateTrigger())
};
}
catch
{
if (throwExceptions)
throw;
return false;
}
return true;
}
/// <inheritdoc />
public void Dispose()
public override void Dispose()
{
try { UpdateTrigger.Dispose(); }
catch { /* at least we tried */ }
foreach (IRGBDevice device in Devices)
try { device.Dispose(); }
catch { /* at least we tried */ }
Devices = Enumerable.Empty<IRGBDevice>();
base.Dispose();
try { _sdk?.ReleaseControl(0); }
catch { /* at least we tried */ }

View File

@ -15,16 +15,17 @@ namespace RGB.NET.Devices.Asus
/// Initializes a new instance of the <see cref="T:RGB.NET.Devices.Asus.AsusDramRGBDevice" /> class.
/// </summary>
/// <param name="info">The specific information provided by Asus for the DRAM.</param>
internal AsusDramRGBDevice(AsusRGBDeviceInfo info)
: base(info)
{ }
internal AsusDramRGBDevice(AsusRGBDeviceInfo info, IDeviceUpdateTrigger updateTrigger)
: base(info, updateTrigger)
{
InitializeLayout();
}
#endregion
#region Methods
/// <inheritdoc />
protected override void InitializeLayout()
private void InitializeLayout()
{
int ledCount = DeviceInfo.Device.Lights.Count;
for (int i = 0; i < ledCount; i++)

View File

@ -1,75 +1,23 @@
using System.Collections.Generic;
using System.Linq;
using RGB.NET.Core;
using RGB.NET.Core;
namespace RGB.NET.Devices.Asus
{
/// <inheritdoc cref="AbstractRGBDevice{TDeviceInfo}" />
/// <inheritdoc cref="IAsusRGBDevice" />
/// <summary>
/// Represents a generic Asus-device. (keyboard, mouse, headset, mousepad).
/// </summary>
public abstract class AsusRGBDevice<TDeviceInfo> : AbstractRGBDevice<TDeviceInfo>, IAsusRGBDevice
where TDeviceInfo : AsusRGBDeviceInfo
{
#region Properties & Fields
/// <inheritdoc />
/// <summary>
/// Gets information about the <see cref="T:RGB.NET.Devices.Asus.AsusRGBDevice" />.
/// </summary>
public override TDeviceInfo DeviceInfo { get; }
/// <summary>
/// Gets or sets the update queue performing updates for this device.
/// </summary>
// ReSharper disable once MemberCanBePrivate.Global
protected AsusUpdateQueue? UpdateQueue { get; set; }
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="AsusRGBDevice{TDeviceInfo}"/> class.
/// </summary>
/// <param name="info">The generic information provided by Asus for the device.</param>
protected AsusRGBDevice(TDeviceInfo info)
{
this.DeviceInfo = info;
}
#endregion
#region Methods
/// <summary>
/// Initializes the device.
/// </summary>
public void Initialize(IDeviceUpdateTrigger updateTrigger)
{
InitializeLayout();
UpdateQueue = new AsusUpdateQueue(updateTrigger);
UpdateQueue.Initialize(DeviceInfo.Device);
}
/// <summary>
/// Initializes the <see cref="Led"/> and <see cref="Size"/> of the device.
/// </summary>
protected abstract void InitializeLayout();
/// <inheritdoc />
protected override void UpdateLeds(IEnumerable<Led> ledsToUpdate) => UpdateQueue?.SetData(ledsToUpdate.Where(x => x.Color.A > 0));
/// <inheritdoc />
public override void Dispose()
{
try { UpdateQueue?.Dispose(); }
catch { /* at least we tried */ }
base.Dispose();
}
protected AsusRGBDevice(TDeviceInfo info, IDeviceUpdateTrigger updateTrigger)
: base(info, new AsusUpdateQueue(updateTrigger, info.Device))
{ }
#endregion
}

View File

@ -21,18 +21,19 @@ namespace RGB.NET.Devices.Asus
/// Initializes a new instance of the <see cref="T:RGB.NET.Devices.Asus.AsusHeadsetRGBDevice" /> class.
/// </summary>
/// <param name="info">The specific information provided by Asus for the headset.</param>
internal AsusUnspecifiedRGBDevice(AsusRGBDeviceInfo info, LedId baseLedId)
: base(info)
internal AsusUnspecifiedRGBDevice(AsusRGBDeviceInfo info, LedId baseLedId, IDeviceUpdateTrigger updateTrigger)
: base(info, updateTrigger)
{
this._baseLedId = baseLedId;
InitializeLayout();
}
#endregion
#region Methods
/// <inheritdoc />
protected override void InitializeLayout()
private void InitializeLayout()
{
int ledCount = DeviceInfo.Device.Lights.Count;
for (int i = 0; i < ledCount; i++)
@ -41,6 +42,7 @@ namespace RGB.NET.Devices.Asus
/// <inheritdoc />
protected override object? GetLedCustomData(LedId ledId) => (int)ledId - (int)_baseLedId;
#endregion
}
}

View File

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System;
using AuraServiceLib;
using RGB.NET.Core;
@ -15,7 +15,7 @@ namespace RGB.NET.Devices.Asus
/// <summary>
/// The device to be updated.
/// </summary>
protected IAuraSyncDevice? Device { get; private set; }
protected IAuraSyncDevice Device { get; }
#endregion
@ -25,28 +25,19 @@ namespace RGB.NET.Devices.Asus
/// Initializes a new instance of the <see cref="AsusUpdateQueue"/> class.
/// </summary>
/// <param name="updateTrigger">The update trigger used by this queue.</param>
public AsusUpdateQueue(IDeviceUpdateTrigger updateTrigger)
public AsusUpdateQueue(IDeviceUpdateTrigger updateTrigger, IAuraSyncDevice device)
: base(updateTrigger)
{ }
{
this.Device = device;
}
#endregion
#region Methods
/// <summary>
/// Initializes the queue.
/// </summary>
/// <param name="device">The device to be updated.</param>
public void Initialize(IAuraSyncDevice device)
{
Device = device;
}
/// <inheritdoc />
protected override void Update(Dictionary<object, Color> dataSet)
protected override void Update(in ReadOnlySpan<(object key, Color color)> dataSet)
{
if (Device == null) return;
try
{
if ((Device.Type == (uint)AsusDeviceType.KEYBOARD_RGB) || (Device.Type == (uint)AsusDeviceType.NB_KB_RGB))

View File

@ -5,8 +5,6 @@ namespace RGB.NET.Devices.Asus
/// <summary>
/// Represents a asus RGB-device.
/// </summary>
internal interface IAsusRGBDevice : IRGBDevice
{
void Initialize(IDeviceUpdateTrigger updateTrigger);
}
public interface IAsusRGBDevice : IRGBDevice
{ }
}

View File

@ -15,16 +15,17 @@ namespace RGB.NET.Devices.Asus
/// Initializes a new instance of the <see cref="T:RGB.NET.Devices.Asus.AsusGraphicsCardRGBDevice" /> class.
/// </summary>
/// <param name="info">The specific information provided by Asus for the graphics card.</param>
internal AsusGraphicsCardRGBDevice(AsusRGBDeviceInfo info)
: base(info)
{ }
internal AsusGraphicsCardRGBDevice(AsusRGBDeviceInfo info, IDeviceUpdateTrigger updateTrigger)
: base(info, updateTrigger)
{
InitializeLayout();
}
#endregion
#region Methods
/// <inheritdoc />
protected override void InitializeLayout()
private void InitializeLayout()
{
int ledCount = DeviceInfo.Device.Lights.Count;
for (int i = 0; i < ledCount; i++)

View File

@ -15,16 +15,17 @@ namespace RGB.NET.Devices.Asus
/// Initializes a new instance of the <see cref="T:RGB.NET.Devices.Asus.AsusHeadsetRGBDevice" /> class.
/// </summary>
/// <param name="info">The specific information provided by Asus for the headset.</param>
internal AsusHeadsetRGBDevice(AsusRGBDeviceInfo info)
: base(info)
{ }
internal AsusHeadsetRGBDevice(AsusRGBDeviceInfo info, IDeviceUpdateTrigger updateTrigger)
: base(info, updateTrigger)
{
InitializeLayout();
}
#endregion
#region Methods
/// <inheritdoc />
protected override void InitializeLayout()
private void InitializeLayout()
{
int ledCount = DeviceInfo.Device.Lights.Count;
for (int i = 0; i < ledCount; i++)

View File

@ -24,16 +24,17 @@ namespace RGB.NET.Devices.Asus
/// Initializes a new instance of the <see cref="T:RGB.NET.Devices.Asus.AsusKeyboardRGBDevice" /> class.
/// </summary>
/// <param name="info">The specific information provided by Asus for the keyboard.</param>
internal AsusKeyboardRGBDevice(AsusKeyboardRGBDeviceInfo info)
: base(info)
{ }
internal AsusKeyboardRGBDevice(AsusKeyboardRGBDeviceInfo info, IDeviceUpdateTrigger updateTrigger)
: base(info, updateTrigger)
{
InitializeLayout();
}
#endregion
#region Methods
/// <inheritdoc />
protected override void InitializeLayout()
private void InitializeLayout()
{
Dictionary<AsusLedId, LedId> reversedMapping = AsusKeyboardLedMapping.MAPPING.ToDictionary(x => x.Value, x => x.Key);

View File

@ -15,16 +15,17 @@ namespace RGB.NET.Devices.Asus
/// Initializes a new instance of the <see cref="T:RGB.NET.Devices.Asus.AsusMainboardRGBDevice" /> class.
/// </summary>
/// <param name="info">The specific information provided by Asus for the mainboard.</param>
internal AsusMainboardRGBDevice(AsusRGBDeviceInfo info)
: base(info)
{ }
internal AsusMainboardRGBDevice(AsusRGBDeviceInfo info, IDeviceUpdateTrigger updateTrigger)
: base(info, updateTrigger)
{
InitializeLayout();
}
#endregion
#region Methods
/// <inheritdoc />
protected override void InitializeLayout()
private void InitializeLayout()
{
int ledCount = DeviceInfo.Device.Lights.Count;
for (int i = 0; i < ledCount; i++)
@ -33,7 +34,7 @@ namespace RGB.NET.Devices.Asus
/// <inheritdoc />
protected override object? GetLedCustomData(LedId ledId) => (int)ledId - (int)LedId.Mainboard1;
#endregion
}
}

View File

@ -15,16 +15,17 @@ namespace RGB.NET.Devices.Asus
/// Initializes a new instance of the <see cref="T:RGB.NET.Devices.Asus.AsusMouseRGBDevice" /> class.
/// </summary>
/// <param name="info">The specific information provided by Asus for the mouse.</param>
internal AsusMouseRGBDevice(AsusRGBDeviceInfo info)
: base(info)
{ }
internal AsusMouseRGBDevice(AsusRGBDeviceInfo info, IDeviceUpdateTrigger updateTrigger)
: base(info, updateTrigger)
{
InitializeLayout();
}
#endregion
#region Methods
/// <inheritdoc />
protected override void InitializeLayout()
private void InitializeLayout()
{
int ledCount = DeviceInfo.Device.Lights.Count;
for (int i = 0; i < ledCount; i++)

View File

@ -3,8 +3,6 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using RGB.NET.Core;
using RGB.NET.Devices.CoolerMaster.Helper;
using RGB.NET.Devices.CoolerMaster.Native;
@ -15,7 +13,7 @@ namespace RGB.NET.Devices.CoolerMaster
/// <summary>
/// Represents a device provider responsible for Cooler Master devices.
/// </summary>
public class CoolerMasterDeviceProvider : IRGBDeviceProvider
public class CoolerMasterDeviceProvider : AbstractRGBDeviceProvider
{
#region Properties & Fields
@ -37,20 +35,6 @@ namespace RGB.NET.Devices.CoolerMaster
/// </summary>
public static List<string> PossibleX64NativePaths { get; } = new() { "x64/CMSDK.dll" };
/// <inheritdoc />
/// <summary>
/// Indicates if the SDK is initialized and ready to use.
/// </summary>
public bool IsInitialized { get; private set; }
/// <inheritdoc />
public IEnumerable<IRGBDevice> Devices { get; private set; } = Enumerable.Empty<IRGBDevice>();
/// <summary>
/// The <see cref="DeviceUpdateTrigger"/> used to trigger the updates for cooler master devices.
/// </summary>
public DeviceUpdateTrigger UpdateTrigger { get; }
#endregion
#region Constructors
@ -63,97 +47,48 @@ namespace RGB.NET.Devices.CoolerMaster
{
if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(CoolerMasterDeviceProvider)}");
_instance = this;
UpdateTrigger = new DeviceUpdateTrigger();
}
#endregion
#region Methods
/// <inheritdoc />
public bool Initialize(RGBDeviceType loadFilter = RGBDeviceType.All, bool throwExceptions = false)
protected override void InitializeSDK()
{
IsInitialized = false;
try
{
UpdateTrigger.Stop();
_CoolerMasterSDK.Reload();
if (_CoolerMasterSDK.GetSDKVersion() <= 0) return false;
IList<IRGBDevice> devices = new List<IRGBDevice>();
foreach (CoolerMasterDevicesIndexes index in Enum.GetValues(typeof(CoolerMasterDevicesIndexes)))
{
try
{
RGBDeviceType deviceType = index.GetDeviceType();
if (deviceType == RGBDeviceType.None) continue;
if (_CoolerMasterSDK.IsDevicePlugged(index))
{
if (!loadFilter.HasFlag(deviceType)) continue;
ICoolerMasterRGBDevice device;
switch (deviceType)
{
case RGBDeviceType.Keyboard:
CoolerMasterPhysicalKeyboardLayout physicalLayout = _CoolerMasterSDK.GetDeviceLayout(index);
device = new CoolerMasterKeyboardRGBDevice(new CoolerMasterKeyboardRGBDeviceInfo(index, physicalLayout));
break;
case RGBDeviceType.Mouse:
device = new CoolerMasterMouseRGBDevice(new CoolerMasterMouseRGBDeviceInfo(index));
break;
default:
if (throwExceptions)
throw new RGBDeviceException("Unknown Device-Type");
else
continue;
}
if (!_CoolerMasterSDK.EnableLedControl(true, index))
throw new RGBDeviceException("Failed to enable LED control for device " + index);
device.Initialize(UpdateTrigger);
devices.Add(device);
}
}
catch { if (throwExceptions) throw; }
}
UpdateTrigger.Start();
Devices = new ReadOnlyCollection<IRGBDevice>(devices);
IsInitialized = true;
}
catch
{
if (throwExceptions)
throw;
return false;
}
return true;
_CoolerMasterSDK.Reload();
if (_CoolerMasterSDK.GetSDKVersion() <= 0) Throw(new RGBDeviceException("Failed to initialize CoolerMaster-SDK"));
}
/// <inheritdoc />
public void Dispose()
protected override IEnumerable<IRGBDevice> LoadDevices()
{
try { UpdateTrigger.Dispose(); }
catch { /* at least we tried */ }
foreach (CoolerMasterDevicesIndexes index in Enum.GetValues(typeof(CoolerMasterDevicesIndexes)))
{
RGBDeviceType deviceType = index.GetDeviceType();
if (deviceType == RGBDeviceType.None) continue;
foreach (IRGBDevice device in Devices)
try { device.Dispose(); }
catch { /* at least we tried */ }
Devices = Enumerable.Empty<IRGBDevice>();
if (_CoolerMasterSDK.IsDevicePlugged(index))
{
if (!_CoolerMasterSDK.EnableLedControl(true, index))
Throw(new RGBDeviceException("Failed to enable LED control for device " + index));
else
{
switch (deviceType)
{
case RGBDeviceType.Keyboard:
yield return new CoolerMasterKeyboardRGBDevice(new CoolerMasterKeyboardRGBDeviceInfo(index, _CoolerMasterSDK.GetDeviceLayout(index)), GetUpdateTrigger());
break;
// DarthAffe 03.03.2020: Should be done but isn't possible due to an weird winodws-hook inside the sdk which corrupts the stack when unloading the dll
//try { _CoolerMasterSDK.UnloadCMSDK(); }
//catch { /* at least we tried */ }
case RGBDeviceType.Mouse:
yield return new CoolerMasterMouseRGBDevice(new CoolerMasterMouseRGBDeviceInfo(index), GetUpdateTrigger());
break;
default:
Throw(new RGBDeviceException("Unknown Device-Type"));
break;
}
}
}
}
}
#endregion

View File

@ -1,76 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using RGB.NET.Core;
using RGB.NET.Devices.CoolerMaster.Native;
namespace RGB.NET.Devices.CoolerMaster
{
/// <inheritdoc cref="AbstractRGBDevice{TDeviceInfo}" />
/// <inheritdoc cref="ICoolerMasterRGBDevice" />
/// <summary>
/// Represents a generic CoolerMaster-device. (keyboard, mouse, headset, mousepad).
/// </summary>
public abstract class CoolerMasterRGBDevice<TDeviceInfo> : AbstractRGBDevice<TDeviceInfo>, ICoolerMasterRGBDevice
where TDeviceInfo : CoolerMasterRGBDeviceInfo
{
#region Properties & Fields
/// <inheritdoc />
/// <summary>
/// Gets information about the <see cref="T:RGB.NET.Devices.CoolerMaster.CoolerMasterRGBDevice" />.
/// </summary>
public override TDeviceInfo DeviceInfo { get; }
/// <summary>
/// Gets or sets the update queue performing updates for this device.
/// </summary>
// ReSharper disable once MemberCanBePrivate.Global
protected CoolerMasterUpdateQueue? UpdateQueue { get; set; }
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="CoolerMasterRGBDevice{TDeviceInfo}"/> class.
/// </summary>
/// <param name="info">The generic information provided by CoolerMaster for the device.</param>
protected CoolerMasterRGBDevice(TDeviceInfo info)
{
this.DeviceInfo = info;
}
protected CoolerMasterRGBDevice(TDeviceInfo info, IDeviceUpdateTrigger updateTrigger)
: base(info, new CoolerMasterUpdateQueue(updateTrigger, info.DeviceIndex))
{ }
#endregion
#region Methods
/// <summary>
/// Initializes the device.
/// </summary>
public void Initialize(IDeviceUpdateTrigger updateTrigger)
{
InitializeLayout();
UpdateQueue = new CoolerMasterUpdateQueue(updateTrigger, DeviceInfo.DeviceIndex);
}
/// <summary>
/// Initializes the <see cref="Led"/> and <see cref="Size"/> of the device.
/// </summary>
protected abstract void InitializeLayout();
/// <inheritdoc />
protected override void UpdateLeds(IEnumerable<Led> ledsToUpdate) => UpdateQueue?.SetData(ledsToUpdate.Where(x => x.Color.A > 0));
/// <inheritdoc cref="IDisposable.Dispose" />
/// <inheritdoc cref="AbstractRGBDevice{TDeviceInfo}.Dispose" />
public override void Dispose()
{
try { UpdateQueue?.Dispose(); }
catch { /* at least we tried */ }
_CoolerMasterSDK.EnableLedControl(false, DeviceInfo.DeviceIndex);
base.Dispose();

View File

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System;
using RGB.NET.Core;
using RGB.NET.Devices.CoolerMaster.Native;
@ -28,7 +28,7 @@ namespace RGB.NET.Devices.CoolerMaster
: base(updateTrigger)
{
this._deviceIndex = deviceIndex;
_deviceMatrix = new _CoolerMasterColorMatrix();
_deviceMatrix.KeyColor = new _CoolerMasterKeyColor[_CoolerMasterColorMatrix.ROWS, _CoolerMasterColorMatrix.COLUMNS];
}
@ -38,12 +38,12 @@ namespace RGB.NET.Devices.CoolerMaster
#region Methods
/// <inheritdoc />
protected override void Update(Dictionary<object, Color> dataSet)
protected override void Update(in ReadOnlySpan<(object key, Color color)> dataSet)
{
foreach (KeyValuePair<object, Color> data in dataSet)
foreach ((object key, Color color) in dataSet)
{
(int row, int column) = ((int, int))data.Key;
_deviceMatrix.KeyColor[row, column] = new _CoolerMasterKeyColor(data.Value.GetR(), data.Value.GetG(), data.Value.GetB());
(int row, int column) = ((int, int))key;
_deviceMatrix.KeyColor[row, column] = new _CoolerMasterKeyColor(color.GetR(), color.GetG(), color.GetB());
}
_CoolerMasterSDK.SetAllLedColor(_deviceMatrix, _deviceIndex);

View File

@ -5,8 +5,6 @@ namespace RGB.NET.Devices.CoolerMaster
/// <summary>
/// Represents a CoolerMaster RGB-device.
/// </summary>
internal interface ICoolerMasterRGBDevice : IRGBDevice
{
void Initialize(IDeviceUpdateTrigger updateTrigger);
}
public interface ICoolerMasterRGBDevice : IRGBDevice
{ }
}

View File

@ -22,16 +22,17 @@ namespace RGB.NET.Devices.CoolerMaster
/// Initializes a new instance of the <see cref="T:RGB.NET.Devices.CoolerMaster.CoolerMasterKeyboardRGBDevice" /> class.
/// </summary>
/// <param name="info">The specific information provided by CoolerMaster for the keyboard</param>
internal CoolerMasterKeyboardRGBDevice(CoolerMasterKeyboardRGBDeviceInfo info)
: base(info)
{ }
internal CoolerMasterKeyboardRGBDevice(CoolerMasterKeyboardRGBDeviceInfo info, IDeviceUpdateTrigger updateTrigger)
: base(info, updateTrigger)
{
InitializeLayout();
}
#endregion
#region Methods
/// <inheritdoc />
protected override void InitializeLayout()
private void InitializeLayout()
{
if (!CoolerMasterKeyboardLedMappings.Mapping.TryGetValue(DeviceInfo.DeviceIndex, out Dictionary<CoolerMasterPhysicalKeyboardLayout, Dictionary<LedId, (int row, int column)>>? deviceMappings))
throw new RGBDeviceException($"Failed to find a CoolerMasterKeyboardLedMapping for device index {DeviceInfo.DeviceIndex}");
@ -44,7 +45,7 @@ namespace RGB.NET.Devices.CoolerMaster
/// <inheritdoc />
protected override object GetLedCustomData(LedId ledId) => CoolerMasterKeyboardLedMappings.Mapping[DeviceInfo.DeviceIndex][DeviceInfo.PhysicalLayout][ledId];
#endregion
}
}

View File

@ -16,16 +16,15 @@ namespace RGB.NET.Devices.CoolerMaster
/// Initializes a new instance of the <see cref="T:RGB.NET.Devices.CoolerMaster.CoolerMasterMouseRGBDevice" /> class.
/// </summary>
/// <param name="info">The specific information provided by CoolerMaster for the mouse</param>
internal CoolerMasterMouseRGBDevice(CoolerMasterMouseRGBDeviceInfo info)
: base(info)
internal CoolerMasterMouseRGBDevice(CoolerMasterMouseRGBDeviceInfo info, IDeviceUpdateTrigger updateTrigger)
: base(info, updateTrigger)
{ }
#endregion
#region Methods
/// <inheritdoc />
protected override void InitializeLayout()
private void InitializeLayout()
{
Dictionary<LedId, (int row, int column)> mapping = CoolerMasterMouseLedMappings.Mapping[DeviceInfo.DeviceIndex];
@ -35,7 +34,7 @@ namespace RGB.NET.Devices.CoolerMaster
/// <inheritdoc />
protected override object GetLedCustomData(LedId ledId) => CoolerMasterMouseLedMappings.Mapping[DeviceInfo.DeviceIndex][ledId];
#endregion
}
}

View File

@ -15,7 +15,7 @@ namespace RGB.NET.Devices.Corsair
/// <summary>
/// Represents a device provider responsible for corsair (CUE) devices.
/// </summary>
public class CorsairDeviceProvider : IRGBDeviceProvider
public class CorsairDeviceProvider : AbstractRGBDeviceProvider
{
#region Properties & Fields
@ -37,12 +37,6 @@ namespace RGB.NET.Devices.Corsair
/// </summary>
public static List<string> PossibleX64NativePaths { get; } = new() { "x64/CUESDK.dll", "x64/CUESDK_2015.dll", "x64/CUESDK_2013.dll" };
/// <inheritdoc />
/// <summary>
/// Indicates if the SDK is initialized and ready to use.
/// </summary>
public bool IsInitialized { get; private set; }
/// <summary>
/// Gets the protocol details for the current SDK-connection.
/// </summary>
@ -53,14 +47,6 @@ namespace RGB.NET.Devices.Corsair
/// </summary>
public CorsairError LastError => _CUESDK.CorsairGetLastError();
/// <inheritdoc />
public IEnumerable<IRGBDevice> Devices { get; private set; } = Enumerable.Empty<IRGBDevice>();
/// <summary>
/// The <see cref="DeviceUpdateTrigger"/> used to trigger the updates for corsair devices.
/// </summary>
public DeviceUpdateTrigger UpdateTrigger { get; }
#endregion
#region Constructors
@ -73,156 +59,108 @@ namespace RGB.NET.Devices.Corsair
{
if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(CorsairDeviceProvider)}");
_instance = this;
UpdateTrigger = new DeviceUpdateTrigger();
}
#endregion
#region Methods
/// <inheritdoc />
/// <exception cref="RGBDeviceException">Thrown if the SDK is already initialized or if the SDK is not compatible to CUE.</exception>
/// <exception cref="CUEException">Thrown if the CUE-SDK provides an error.</exception>
public bool Initialize(RGBDeviceType loadFilter = RGBDeviceType.All, bool throwExceptions = false)
protected override void InitializeSDK()
{
IsInitialized = false;
_CUESDK.Reload();
try
{
UpdateTrigger.Stop();
ProtocolDetails = new CorsairProtocolDetails(_CUESDK.CorsairPerformProtocolHandshake());
_CUESDK.Reload();
CorsairError error = LastError;
if (error != CorsairError.Success)
Throw(new CUEException(error));
ProtocolDetails = new CorsairProtocolDetails(_CUESDK.CorsairPerformProtocolHandshake());
if (ProtocolDetails.BreakingChanges)
Throw(new RGBDeviceException("The SDK currently used isn't compatible with the installed version of CUE.\r\n"
+ $"CUE-Version: {ProtocolDetails.ServerVersion} (Protocol {ProtocolDetails.ServerProtocolVersion})\r\n"
+ $"SDK-Version: {ProtocolDetails.SdkVersion} (Protocol {ProtocolDetails.SdkProtocolVersion})"));
CorsairError error = LastError;
if (error != CorsairError.Success)
throw new CUEException(error);
if (ProtocolDetails.BreakingChanges)
throw new RGBDeviceException("The SDK currently used isn't compatible with the installed version of CUE.\r\n"
+ $"CUE-Version: {ProtocolDetails.ServerVersion} (Protocol {ProtocolDetails.ServerProtocolVersion})\r\n"
+ $"SDK-Version: {ProtocolDetails.SdkVersion} (Protocol {ProtocolDetails.SdkProtocolVersion})");
// DarthAffe 02.02.2021: 127 is iCUE
if (!_CUESDK.CorsairSetLayerPriority(128))
throw new CUEException(LastError);
Dictionary<string, int> modelCounter = new();
IList<IRGBDevice> devices = new List<IRGBDevice>();
int deviceCount = _CUESDK.CorsairGetDeviceCount();
for (int i = 0; i < deviceCount; i++)
{
try
{
_CorsairDeviceInfo nativeDeviceInfo = (_CorsairDeviceInfo)Marshal.PtrToStructure(_CUESDK.CorsairGetDeviceInfo(i), typeof(_CorsairDeviceInfo))!;
CorsairRGBDeviceInfo info = new(i, RGBDeviceType.Unknown, nativeDeviceInfo, modelCounter);
if (!info.CapsMask.HasFlag(CorsairDeviceCaps.Lighting))
continue; // Everything that doesn't support lighting control is useless
CorsairDeviceUpdateQueue? deviceUpdateQueue = null;
foreach (ICorsairRGBDevice device in GetRGBDevice(info, i, nativeDeviceInfo, modelCounter))
{
if ((device == null) || !loadFilter.HasFlag(device.DeviceInfo.DeviceType)) continue;
deviceUpdateQueue ??= new CorsairDeviceUpdateQueue(UpdateTrigger, info.CorsairDeviceIndex);
device.Initialize(deviceUpdateQueue);
error = LastError;
if (error != CorsairError.Success)
throw new CUEException(error);
devices.Add(device);
}
}
catch { if (throwExceptions) throw; }
}
UpdateTrigger.Start();
Devices = new ReadOnlyCollection<IRGBDevice>(devices);
IsInitialized = true;
}
catch
{
Reset();
if (throwExceptions) throw;
return false;
}
return true;
// DarthAffe 02.02.2021: 127 is iCUE
if (!_CUESDK.CorsairSetLayerPriority(128))
Throw(new CUEException(LastError));
}
private static IEnumerable<ICorsairRGBDevice> GetRGBDevice(CorsairRGBDeviceInfo info, int i, _CorsairDeviceInfo nativeDeviceInfo, Dictionary<string, int> modelCounter)
protected override IEnumerable<IRGBDevice> LoadDevices()
{
switch (info.CorsairDeviceType)
Dictionary<string, int> modelCounter = new();
int deviceCount = _CUESDK.CorsairGetDeviceCount();
for (int i = 0; i < deviceCount; i++)
{
case CorsairDeviceType.Keyboard:
yield return new CorsairKeyboardRGBDevice(new CorsairKeyboardRGBDeviceInfo(i, nativeDeviceInfo, modelCounter));
break;
_CorsairDeviceInfo nativeDeviceInfo = (_CorsairDeviceInfo)Marshal.PtrToStructure(_CUESDK.CorsairGetDeviceInfo(i), typeof(_CorsairDeviceInfo))!;
CorsairRGBDeviceInfo info = new(i, RGBDeviceType.Unknown, nativeDeviceInfo, modelCounter);
if (!info.CapsMask.HasFlag(CorsairDeviceCaps.Lighting))
continue; // Everything that doesn't support lighting control is useless
case CorsairDeviceType.Mouse:
yield return new CorsairMouseRGBDevice(new CorsairMouseRGBDeviceInfo(i, nativeDeviceInfo, modelCounter));
break;
CorsairDeviceUpdateQueue updateQueue = new(GetUpdateTrigger(), info.CorsairDeviceIndex);
switch (info.CorsairDeviceType)
{
case CorsairDeviceType.Keyboard:
yield return new CorsairKeyboardRGBDevice(new CorsairKeyboardRGBDeviceInfo(i, nativeDeviceInfo, modelCounter), updateQueue);
break;
case CorsairDeviceType.Headset:
yield return new CorsairHeadsetRGBDevice(new CorsairHeadsetRGBDeviceInfo(i, nativeDeviceInfo, modelCounter));
break;
case CorsairDeviceType.Mouse:
yield return new CorsairMouseRGBDevice(new CorsairMouseRGBDeviceInfo(i, nativeDeviceInfo, modelCounter), updateQueue);
break;
case CorsairDeviceType.Mousepad:
yield return new CorsairMousepadRGBDevice(new CorsairMousepadRGBDeviceInfo(i, nativeDeviceInfo, modelCounter));
break;
case CorsairDeviceType.Headset:
yield return new CorsairHeadsetRGBDevice(new CorsairHeadsetRGBDeviceInfo(i, nativeDeviceInfo, modelCounter), updateQueue);
break;
case CorsairDeviceType.HeadsetStand:
yield return new CorsairHeadsetStandRGBDevice(new CorsairHeadsetStandRGBDeviceInfo(i, nativeDeviceInfo, modelCounter));
break;
case CorsairDeviceType.Mousepad:
yield return new CorsairMousepadRGBDevice(new CorsairMousepadRGBDeviceInfo(i, nativeDeviceInfo, modelCounter), updateQueue);
break;
case CorsairDeviceType.MemoryModule:
yield return new CorsairMemoryRGBDevice(new CorsairMemoryRGBDeviceInfo(i, nativeDeviceInfo, modelCounter));
break;
case CorsairDeviceType.HeadsetStand:
yield return new CorsairHeadsetStandRGBDevice(new CorsairHeadsetStandRGBDeviceInfo(i, nativeDeviceInfo, modelCounter), updateQueue);
break;
case CorsairDeviceType.Cooler:
case CorsairDeviceType.CommanderPro:
case CorsairDeviceType.LightningNodePro:
_CorsairChannelsInfo? channelsInfo = nativeDeviceInfo.channels;
if (channelsInfo != null)
{
IntPtr channelInfoPtr = channelsInfo.channels;
case CorsairDeviceType.MemoryModule:
yield return new CorsairMemoryRGBDevice(new CorsairMemoryRGBDeviceInfo(i, nativeDeviceInfo, modelCounter), updateQueue);
break;
for (int channel = 0; channel < channelsInfo.channelsCount; channel++)
case CorsairDeviceType.Cooler:
case CorsairDeviceType.CommanderPro:
case CorsairDeviceType.LightningNodePro:
_CorsairChannelsInfo? channelsInfo = nativeDeviceInfo.channels;
if (channelsInfo != null)
{
CorsairLedId referenceLed = GetChannelReferenceId(info.CorsairDeviceType, channel);
if (referenceLed == CorsairLedId.Invalid) continue;
IntPtr channelInfoPtr = channelsInfo.channels;
_CorsairChannelInfo channelInfo = (_CorsairChannelInfo)Marshal.PtrToStructure(channelInfoPtr, typeof(_CorsairChannelInfo))!;
int channelDeviceInfoStructSize = Marshal.SizeOf(typeof(_CorsairChannelDeviceInfo));
IntPtr channelDeviceInfoPtr = channelInfo.devices;
for (int device = 0; device < channelInfo.devicesCount; device++)
for (int channel = 0; channel < channelsInfo.channelsCount; channel++)
{
_CorsairChannelDeviceInfo channelDeviceInfo = (_CorsairChannelDeviceInfo)Marshal.PtrToStructure(channelDeviceInfoPtr, typeof(_CorsairChannelDeviceInfo))!;
CorsairLedId referenceLed = GetChannelReferenceId(info.CorsairDeviceType, channel);
if (referenceLed == CorsairLedId.Invalid) continue;
yield return new CorsairCustomRGBDevice(new CorsairCustomRGBDeviceInfo(info, nativeDeviceInfo, channelDeviceInfo, referenceLed, modelCounter));
referenceLed += channelDeviceInfo.deviceLedCount;
_CorsairChannelInfo channelInfo = (_CorsairChannelInfo)Marshal.PtrToStructure(channelInfoPtr, typeof(_CorsairChannelInfo))!;
channelDeviceInfoPtr = new IntPtr(channelDeviceInfoPtr.ToInt64() + channelDeviceInfoStructSize);
int channelDeviceInfoStructSize = Marshal.SizeOf(typeof(_CorsairChannelDeviceInfo));
IntPtr channelDeviceInfoPtr = channelInfo.devices;
for (int device = 0; device < channelInfo.devicesCount; device++)
{
_CorsairChannelDeviceInfo channelDeviceInfo = (_CorsairChannelDeviceInfo)Marshal.PtrToStructure(channelDeviceInfoPtr, typeof(_CorsairChannelDeviceInfo))!;
yield return new CorsairCustomRGBDevice(new CorsairCustomRGBDeviceInfo(info, nativeDeviceInfo, channelDeviceInfo, referenceLed, modelCounter), updateQueue);
referenceLed += channelDeviceInfo.deviceLedCount;
channelDeviceInfoPtr = new IntPtr(channelDeviceInfoPtr.ToInt64() + channelDeviceInfoStructSize);
}
int channelInfoStructSize = Marshal.SizeOf(typeof(_CorsairChannelInfo));
channelInfoPtr = new IntPtr(channelInfoPtr.ToInt64() + channelInfoStructSize);
}
int channelInfoStructSize = Marshal.SizeOf(typeof(_CorsairChannelInfo));
channelInfoPtr = new IntPtr(channelInfoPtr.ToInt64() + channelInfoStructSize);
}
}
break;
break;
// ReSharper disable once RedundantCaseLabel
case CorsairDeviceType.Unknown:
default:
throw new RGBDeviceException("Unknown Device-Type");
default:
Throw(new RGBDeviceException("Unknown Device-Type"));
break;
}
}
}
@ -240,23 +178,17 @@ namespace RGB.NET.Devices.Corsair
};
}
private void Reset()
protected override void Reset()
{
ProtocolDetails = null;
Devices = Enumerable.Empty<IRGBDevice>();
IsInitialized = false;
base.Reset();
}
/// <inheritdoc />
public void Dispose()
public override void Dispose()
{
try { UpdateTrigger.Dispose(); }
catch { /* at least we tried */ }
foreach (IRGBDevice device in Devices)
try { device.Dispose(); }
catch { /* at least we tried */ }
Devices = Enumerable.Empty<IRGBDevice>();
base.Dispose();
try { _CUESDK.UnloadCUESDK(); }
catch { /* at least we tried */ }

View File

@ -25,16 +25,17 @@ namespace RGB.NET.Devices.Corsair
/// Initializes a new instance of the <see cref="T:RGB.NET.Devices.Corsair.CorsairCustomRGBDevice" /> class.
/// </summary>
/// <param name="info">The specific information provided by CUE for the custom-device.</param>
internal CorsairCustomRGBDevice(CorsairCustomRGBDeviceInfo info)
: base(info)
{ }
internal CorsairCustomRGBDevice(CorsairCustomRGBDeviceInfo info, CorsairDeviceUpdateQueue updateQueue)
: base(info, updateQueue)
{
InitializeLayout();
}
#endregion
#region Methods
/// <inheritdoc />
protected override void InitializeLayout()
private void InitializeLayout()
{
LedId referenceId = GetReferenceLed(DeviceInfo.DeviceType);
@ -45,7 +46,7 @@ namespace RGB.NET.Devices.Corsair
AddLed(ledId, new Point(i * 10, 0), new Size(10, 10));
}
}
protected override object GetLedCustomData(LedId ledId) => _idMapping.TryGetValue(ledId, out CorsairLedId id) ? id : CorsairLedId.Invalid;
protected virtual LedId GetReferenceLed(RGBDeviceType deviceType)

View File

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using RGB.NET.Core;
using RGB.NET.Devices.Corsair.Native;
@ -36,26 +35,26 @@ namespace RGB.NET.Devices.Corsair
#region Methods
/// <inheritdoc />
protected override void Update(Dictionary<object, Color> dataSet)
protected override void Update(in ReadOnlySpan<(object key, Color color)> dataSet)
{
int structSize = Marshal.SizeOf(typeof(_CorsairLedColor));
IntPtr ptr = Marshal.AllocHGlobal(structSize * dataSet.Count);
IntPtr ptr = Marshal.AllocHGlobal(structSize * dataSet.Length);
IntPtr addPtr = new(ptr.ToInt64());
foreach (KeyValuePair<object, Color> data in dataSet)
foreach ((object key, Color color) in dataSet)
{
_CorsairLedColor color = new()
{
ledId = (int)data.Key,
r = data.Value.GetR(),
g = data.Value.GetG(),
b = data.Value.GetB()
_CorsairLedColor corsairColor = new()
{
ledId = (int)key,
r = color.GetR(),
g = color.GetG(),
b = color.GetB()
};
Marshal.StructureToPtr(color, addPtr, false);
Marshal.StructureToPtr(corsairColor, addPtr, false);
addPtr = new IntPtr(addPtr.ToInt64() + structSize);
}
_CUESDK.CorsairSetLedsColorsBufferByDeviceIndex(_deviceIndex, dataSet.Count, ptr);
_CUESDK.CorsairSetLedsColorsBufferByDeviceIndex(_deviceIndex, dataSet.Length, ptr);
_CUESDK.CorsairSetLedsColorsFlushBuffer();
Marshal.FreeHGlobal(ptr);
}

View File

@ -1,99 +1,23 @@
using System.Collections.Generic;
using System.Linq;
using RGB.NET.Core;
using RGB.NET.Core;
namespace RGB.NET.Devices.Corsair
{
/// <inheritdoc cref="AbstractRGBDevice{TDeviceInfo}" />
/// <inheritdoc cref="ICorsairRGBDevice" />
/// <summary>
/// Represents a generic CUE-device. (keyboard, mouse, headset, mousepad).
/// </summary>
public abstract class CorsairRGBDevice<TDeviceInfo> : AbstractRGBDevice<TDeviceInfo>, ICorsairRGBDevice
where TDeviceInfo : CorsairRGBDeviceInfo
{
#region Properties & Fields
/// <inheritdoc />
/// <summary>
/// Gets information about the <see cref="T:RGB.NET.Devices.Corsair.CorsairRGBDevice" />.
/// </summary>
public override TDeviceInfo DeviceInfo { get; }
/// <summary>
/// Gets a dictionary containing all <see cref="Led"/> of the <see cref="CorsairRGBDevice{TDeviceInfo}"/>.
/// </summary>
// ReSharper disable once MemberCanBePrivate.Global
protected Dictionary<CorsairLedId, Led> InternalLedMapping { get; } = new();
/// <summary>
/// Gets or sets the update queue performing updates for this device.
/// </summary>
// ReSharper disable once MemberCanBePrivate.Global
protected CorsairDeviceUpdateQueue? DeviceUpdateQueue { get; set; }
#endregion
#region Indexer
/// <summary>
/// Gets the <see cref="Led"/> with the specified <see cref="CorsairLedId"/>.
/// </summary>
/// <param name="ledId">The <see cref="CorsairLedId"/> of the <see cref="Led"/> to get.</param>
/// <returns>The <see cref="Led"/> with the specified <see cref="CorsairLedId"/> or null if no <see cref="Led"/> is found.</returns>
// ReSharper disable once MemberCanBePrivate.Global
public Led? this[CorsairLedId ledId] => InternalLedMapping.TryGetValue(ledId, out Led? led) ? led : null;
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="CorsairRGBDevice{TDeviceInfo}"/> class.
/// </summary>
/// <param name="info">The generic information provided by CUE for the device.</param>
protected CorsairRGBDevice(TDeviceInfo info)
{
this.DeviceInfo = info;
}
#endregion
#region Methods
/// <summary>
/// Initializes the device.
/// </summary>
public void Initialize(CorsairDeviceUpdateQueue deviceUpdateQueue)
{
DeviceUpdateQueue = deviceUpdateQueue;
InitializeLayout();
foreach (Led led in LedMapping.Values)
{
if (led.CustomData is CorsairLedId ledId && (ledId != CorsairLedId.Invalid))
InternalLedMapping.Add(ledId, led);
}
}
/// <summary>
/// Initializes the <see cref="Led"/> and <see cref="Size"/> of the device.
/// </summary>
protected abstract void InitializeLayout();
/// <inheritdoc />
protected override void UpdateLeds(IEnumerable<Led> ledsToUpdate)
=> DeviceUpdateQueue?.SetData(ledsToUpdate.Where(x => (x.Color.A > 0) && (x.CustomData is CorsairLedId ledId && (ledId != CorsairLedId.Invalid))));
/// <inheritdoc />
public override void Dispose()
{
try { DeviceUpdateQueue?.Dispose(); }
catch { /* at least we tried */ }
base.Dispose();
}
protected CorsairRGBDevice(TDeviceInfo info, CorsairDeviceUpdateQueue updateQueue)
: base(info, updateQueue)
{ }
#endregion
}

View File

@ -5,8 +5,6 @@ namespace RGB.NET.Devices.Corsair
/// <summary>
/// Represents a corsair RGB-device.
/// </summary>
internal interface ICorsairRGBDevice : IRGBDevice
{
void Initialize(CorsairDeviceUpdateQueue deviceUpdateQueue);
}
public interface ICorsairRGBDevice : IRGBDevice
{ }
}

View File

@ -18,16 +18,17 @@ namespace RGB.NET.Devices.Corsair
/// Initializes a new instance of the <see cref="T:RGB.NET.Devices.Corsair.CorsairHeadsetRGBDevice" /> class.
/// </summary>
/// <param name="info">The specific information provided by CUE for the headset</param>
internal CorsairHeadsetRGBDevice(CorsairHeadsetRGBDeviceInfo info)
: base(info)
{ }
internal CorsairHeadsetRGBDevice(CorsairHeadsetRGBDeviceInfo info, CorsairDeviceUpdateQueue updateQueue)
: base(info, updateQueue)
{
InitializeLayout();
}
#endregion
#region Methods
/// <inheritdoc />
protected override void InitializeLayout()
private void InitializeLayout()
{
AddLed(LedId.Headset1, new Point(0, 0), new Size(10, 10));
AddLed(LedId.Headset2, new Point(10, 0), new Size(10, 10));

View File

@ -23,16 +23,17 @@ namespace RGB.NET.Devices.Corsair
/// Initializes a new instance of the <see cref="T:RGB.NET.Devices.Corsair.CorsairHeadsetStandRGBDevice" /> class.
/// </summary>
/// <param name="info">The specific information provided by CUE for the headset stand</param>
internal CorsairHeadsetStandRGBDevice(CorsairHeadsetStandRGBDeviceInfo info)
: base(info)
{ }
internal CorsairHeadsetStandRGBDevice(CorsairHeadsetStandRGBDeviceInfo info, CorsairDeviceUpdateQueue updateQueue)
: base(info, updateQueue)
{
InitializeLayout();
}
#endregion
#region Methods
/// <inheritdoc />
protected override void InitializeLayout()
private void InitializeLayout()
{
_CorsairLedPositions? nativeLedPositions = (_CorsairLedPositions?)Marshal.PtrToStructure(_CUESDK.CorsairGetLedPositionsByDeviceIndex(DeviceInfo.CorsairDeviceIndex), typeof(_CorsairLedPositions));
if (nativeLedPositions == null) return;

View File

@ -8,10 +8,10 @@ namespace RGB.NET.Devices.Corsair
internal static Rectangle ToRectangle(this _CorsairLedPosition position)
{
//HACK DarthAffe 08.07.2018: It seems like corsair introduced a bug here - it's always 0.
double width = position.width < 0.5 ? 10 : position.width;
double height = position.height < 0.5 ? 10 : position.height;
double posX = position.left;
double posY = position.top;
float width = position.width < 0.5f ? 10 : (float)position.width;
float height = position.height < 0.5f ? 10 : (float)position.height;
float posX = (float)position.left;
float posY = (float)position.top;
return new Rectangle(posX, posY, width, height);
}

View File

@ -28,16 +28,17 @@ namespace RGB.NET.Devices.Corsair
/// Initializes a new instance of the <see cref="T:RGB.NET.Devices.Corsair.CorsairKeyboardRGBDevice" /> class.
/// </summary>
/// <param name="info">The specific information provided by CUE for the keyboard</param>
internal CorsairKeyboardRGBDevice(CorsairKeyboardRGBDeviceInfo info)
: base(info)
{ }
internal CorsairKeyboardRGBDevice(CorsairKeyboardRGBDeviceInfo info, CorsairDeviceUpdateQueue updateQueue)
: base(info, updateQueue)
{
InitializeLayout();
}
#endregion
#region Methods
/// <inheritdoc />
protected override void InitializeLayout()
private void InitializeLayout()
{
_CorsairLedPositions? nativeLedPositions = (_CorsairLedPositions?)Marshal.PtrToStructure(_CUESDK.CorsairGetLedPositionsByDeviceIndex(DeviceInfo.CorsairDeviceIndex), typeof(_CorsairLedPositions));
if (nativeLedPositions == null) return;

View File

@ -22,16 +22,17 @@ namespace RGB.NET.Devices.Corsair
/// Initializes a new instance of the <see cref="T:RGB.NET.Devices.Corsair.CorsairMemoryRGBDevice" /> class.
/// </summary>
/// <param name="info">The specific information provided by CUE for the memory.</param>
internal CorsairMemoryRGBDevice(CorsairMemoryRGBDeviceInfo info)
: base(info)
{ }
internal CorsairMemoryRGBDevice(CorsairMemoryRGBDeviceInfo info, CorsairDeviceUpdateQueue updateQueue)
: base(info, updateQueue)
{
InitializeLayout();
}
#endregion
#region Methods
/// <inheritdoc />
protected override void InitializeLayout()
private void InitializeLayout()
{
_CorsairLedPositions? nativeLedPositions = (_CorsairLedPositions?)Marshal.PtrToStructure(_CUESDK.CorsairGetLedPositionsByDeviceIndex(DeviceInfo.CorsairDeviceIndex), typeof(_CorsairLedPositions));
if (nativeLedPositions == null) return;

View File

@ -19,16 +19,17 @@ namespace RGB.NET.Devices.Corsair
/// Initializes a new instance of the <see cref="T:RGB.NET.Devices.Corsair.CorsairMouseRGBDevice" /> class.
/// </summary>
/// <param name="info">The specific information provided by CUE for the mouse</param>
internal CorsairMouseRGBDevice(CorsairMouseRGBDeviceInfo info)
: base(info)
{ }
internal CorsairMouseRGBDevice(CorsairMouseRGBDeviceInfo info, CorsairDeviceUpdateQueue updateQueue)
: base(info, updateQueue)
{
InitializeLayout();
}
#endregion
#region Methods
/// <inheritdoc />
protected override void InitializeLayout()
private void InitializeLayout()
{
switch (DeviceInfo.PhysicalLayout)
{

View File

@ -23,16 +23,17 @@ namespace RGB.NET.Devices.Corsair
/// Initializes a new instance of the <see cref="T:RGB.NET.Devices.Corsair.CorsairMousepadRGBDevice" /> class.
/// </summary>
/// <param name="info">The specific information provided by CUE for the mousepad</param>
internal CorsairMousepadRGBDevice(CorsairMousepadRGBDeviceInfo info)
: base(info)
{ }
internal CorsairMousepadRGBDevice(CorsairMousepadRGBDeviceInfo info, CorsairDeviceUpdateQueue updateQueue)
: base(info, updateQueue)
{
InitializeLayout();
}
#endregion
#region Methods
/// <inheritdoc />
protected override void InitializeLayout()
private void InitializeLayout()
{
_CorsairLedPositions? nativeLedPositions = (_CorsairLedPositions?)Marshal.PtrToStructure(_CUESDK.CorsairGetLedPositionsByDeviceIndex(DeviceInfo.CorsairDeviceIndex), typeof(_CorsairLedPositions));
if (nativeLedPositions == null) return;

View File

@ -3,8 +3,6 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using RGB.NET.Core;
using RGB.NET.Devices.DMX.E131;
@ -14,7 +12,7 @@ namespace RGB.NET.Devices.DMX
/// <summary>
/// Represents a device provider responsible for DMX devices.
/// </summary>
public class DMXDeviceProvider : IRGBDeviceProvider
public class DMXDeviceProvider : AbstractRGBDeviceProvider
{
#region Properties & Fields
@ -24,22 +22,11 @@ namespace RGB.NET.Devices.DMX
/// </summary>
public static DMXDeviceProvider Instance => _instance ?? new DMXDeviceProvider();
/// <inheritdoc />
public bool IsInitialized { get; private set; }
/// <inheritdoc />
public IEnumerable<IRGBDevice> Devices { get; private set; } = Enumerable.Empty<IRGBDevice>();
/// <summary>
/// Gets a list of all defined device-definitions.
/// </summary>
public List<IDMXDeviceDefinition> DeviceDefinitions { get; } = new();
/// <summary>
/// The <see cref="DeviceUpdateTrigger"/> used to trigger the updates for dmx devices.
/// </summary>
public DeviceUpdateTrigger UpdateTrigger { get; }
#endregion
#region Constructors
@ -52,8 +39,6 @@ namespace RGB.NET.Devices.DMX
{
if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(DMXDeviceProvider)}");
_instance = this;
UpdateTrigger = new DeviceUpdateTrigger();
}
#endregion
@ -66,58 +51,27 @@ namespace RGB.NET.Devices.DMX
/// <param name="deviceDefinition">The <see cref="IDMXDeviceDefinition"/> to add.</param>
public void AddDeviceDefinition(IDMXDeviceDefinition deviceDefinition) => DeviceDefinitions.Add(deviceDefinition);
/// <inheritdoc />
public bool Initialize(RGBDeviceType loadFilter = RGBDeviceType.All, bool throwExceptions = false)
protected override void InitializeSDK() { }
protected override IEnumerable<IRGBDevice> LoadDevices()
{
IsInitialized = false;
try
foreach (IDMXDeviceDefinition dmxDeviceDefinition in DeviceDefinitions)
{
UpdateTrigger.Stop();
IList<IRGBDevice> devices = new List<IRGBDevice>();
foreach (IDMXDeviceDefinition dmxDeviceDefinition in DeviceDefinitions)
IRGBDevice? device = null;
try
{
try
{
if (dmxDeviceDefinition is E131DMXDeviceDefinition e131DMXDeviceDefinition)
{
if (e131DMXDeviceDefinition.Leds.Count > 0)
{
E131Device device = new(new E131DeviceInfo(e131DMXDeviceDefinition), e131DMXDeviceDefinition.Leds);
device.Initialize(UpdateTrigger);
devices.Add(device);
}
}
}
catch { if (throwExceptions) throw; }
if (dmxDeviceDefinition is E131DMXDeviceDefinition e131DMXDeviceDefinition)
if (e131DMXDeviceDefinition.Leds.Count > 0)
device = new E131Device(new E131DeviceInfo(e131DMXDeviceDefinition), e131DMXDeviceDefinition.Leds, GetUpdateTrigger(0));
}
catch (Exception ex)
{
Throw(ex);
}
UpdateTrigger.Start();
Devices = new ReadOnlyCollection<IRGBDevice>(devices);
IsInitialized = true;
if (device != null)
yield return device;
}
catch
{
if (throwExceptions) throw;
return false;
}
return true;
}
/// <inheritdoc />
public void Dispose()
{
try { UpdateTrigger.Dispose(); }
catch { /* at least we tried */ }
foreach (IRGBDevice device in Devices)
try { device.Dispose(); }
catch { /* at least we tried */ }
Devices = Enumerable.Empty<IRGBDevice>();
}
#endregion

View File

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using RGB.NET.Core;
namespace RGB.NET.Devices.DMX.E131
@ -12,55 +11,39 @@ namespace RGB.NET.Devices.DMX.E131
{
#region Properties & Fields
/// <inheritdoc />
public override E131DeviceInfo DeviceInfo { get; }
private readonly Dictionary<LedId, List<(int channel, Func<Color, byte> getValueFunc)>> _ledMappings;
private E131UpdateQueue? _updateQueue;
#endregion
#region Constructors
/// <inheritdoc />
internal E131Device(E131DeviceInfo deviceInfo, Dictionary<LedId, List<(int channel, Func<Color, byte> getValueFunc)>> ledMappings)
internal E131Device(E131DeviceInfo deviceInfo, Dictionary<LedId, List<(int channel, Func<Color, byte> getValueFunc)>> ledMappings, IDeviceUpdateTrigger updateTrigger)
: base(deviceInfo, new E131UpdateQueue(updateTrigger, deviceInfo.Hostname, deviceInfo.Port))
{
this.DeviceInfo = deviceInfo;
this._ledMappings = ledMappings;
InitializeLayout();
E131UpdateQueue updateQueue = (E131UpdateQueue)UpdateQueue;
updateQueue.DataPacket.SetCID(DeviceInfo.CID);
updateQueue.DataPacket.SetUniverse(DeviceInfo.Universe);
}
#endregion
#region Methods
internal void Initialize(IDeviceUpdateTrigger updateTrigger)
private void InitializeLayout()
{
int count = 0;
foreach (LedId id in _ledMappings.Keys)
AddLed(id, new Point((count++) * 10, 0), new Size(10, 10));
_updateQueue = new E131UpdateQueue(updateTrigger, DeviceInfo.Hostname, DeviceInfo.Port);
_updateQueue.DataPacket.SetCID(DeviceInfo.CID);
_updateQueue.DataPacket.SetUniverse(DeviceInfo.Universe);
}
/// <inheritdoc />
protected override object GetLedCustomData(LedId ledId) => new LedChannelMapping(_ledMappings[ledId]);
/// <inheritdoc />
protected override void UpdateLeds(IEnumerable<Led> ledsToUpdate) => _updateQueue?.SetData(ledsToUpdate.Where(x => x.Color.A > 0));
/// <inheritdoc />
public override void Dispose()
{
try { _updateQueue?.Dispose(); }
catch { /* at least we tried */ }
base.Dispose();
}
#endregion
}
}

View File

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using RGB.NET.Core;
@ -52,15 +51,15 @@ namespace RGB.NET.Devices.DMX.E131
#region Methods
/// <inheritdoc />
protected override void Update(Dictionary<object, Color> dataSet)
protected override void Update(in ReadOnlySpan<(object key, Color color)> dataSet)
{
DataPacket.SetSequenceNumber(GetNextSequenceNumber());
foreach (KeyValuePair<object, Color> data in dataSet)
foreach ((object key, Color color) in dataSet)
{
LedChannelMapping mapping = (LedChannelMapping)data.Key;
LedChannelMapping mapping = (LedChannelMapping)key;
foreach ((int channel, Func<Color, byte> getValue) in mapping)
DataPacket.SetChannel(channel, getValue(data.Value));
DataPacket.SetChannel(channel, getValue(color));
}
_socket.Send(DataPacket, DataPacket.Length);

View File

@ -3,8 +3,6 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using RGB.NET.Core;
using RGB.NET.Layout;
@ -14,7 +12,7 @@ namespace RGB.NET.Devices.Debug
/// <summary>
/// Represents a device provider responsible for debug devices.
/// </summary>
public class DebugDeviceProvider : IRGBDeviceProvider
public class DebugDeviceProvider : AbstractRGBDeviceProvider
{
#region Properties & Fields
@ -24,13 +22,7 @@ namespace RGB.NET.Devices.Debug
/// </summary>
public static DebugDeviceProvider Instance => _instance ?? new DebugDeviceProvider();
/// <inheritdoc />
public bool IsInitialized { get; private set; }
/// <inheritdoc />
public IEnumerable<IRGBDevice> Devices { get; private set; } = Enumerable.Empty<IRGBDevice>();
private List<(IDeviceLayout layout, string imageLayout, Action<IEnumerable<Led>>? updateLedsAction)> _fakeDeviceDefinitions = new();
private List<(IDeviceLayout layout, Action<IEnumerable<Led>>? updateLedsAction)> _fakeDeviceDefinitions = new();
#endregion
@ -54,48 +46,28 @@ namespace RGB.NET.Devices.Debug
/// Adds a new fake device definition.
/// </summary>
/// <param name="layout">The path of the layout file to be used.</param>
/// <param name="imageLayout">The image-layout to load.</param>
/// <param name="updateLedsAction">A action emulating led-updates.</param>
public void AddFakeDeviceDefinition(IDeviceLayout layout, string imageLayout, Action<IEnumerable<Led>>? updateLedsAction = null)
=> _fakeDeviceDefinitions.Add((layout, imageLayout, updateLedsAction));
public void AddFakeDeviceDefinition(IDeviceLayout layout, Action<IEnumerable<Led>>? updateLedsAction = null)
=> _fakeDeviceDefinitions.Add((layout, updateLedsAction));
/// <summary>
/// Removes all previously added fake device definitions.
/// </summary>
public void ClearFakeDeviceDefinitions() => _fakeDeviceDefinitions.Clear();
/// <inheritdoc />
public bool Initialize(RGBDeviceType loadFilter = RGBDeviceType.Unknown, bool throwExceptions = false)
protected override void InitializeSDK() { }
protected override IEnumerable<IRGBDevice> LoadDevices()
{
IsInitialized = false;
try
{
List<IRGBDevice> devices = new();
foreach ((IDeviceLayout layout, string imageLayout, Action<IEnumerable<Led>>? updateLedsAction) in _fakeDeviceDefinitions)
{
DebugRGBDevice device = new(layout, updateLedsAction);
devices.Add(device);
}
Devices = new ReadOnlyCollection<IRGBDevice>(devices);
IsInitialized = true;
return true;
}
catch
{
if (throwExceptions) throw;
return false;
}
foreach ((IDeviceLayout layout, Action<IEnumerable<Led>>? updateLedsAction) in _fakeDeviceDefinitions)
yield return new DebugRGBDevice(layout, updateLedsAction);
}
/// <inheritdoc />
public void ResetDevices()
{ }
/// <inheritdoc />
public void Dispose()
public override void Dispose()
{
base.Dispose();
_fakeDeviceDefinitions.Clear();
}

View File

@ -0,0 +1,22 @@
using System;
using RGB.NET.Core;
namespace RGB.NET.Devices.Debug
{
internal class DebugDeviceUpdateQueue : UpdateQueue
{
#region Constructors
public DebugDeviceUpdateQueue()
: base(new DeviceUpdateTrigger())
{ }
#endregion
#region Methods
protected override void Update(in ReadOnlySpan<(object key, Color color)> dataSet) { }
#endregion
}
}

View File

@ -13,9 +13,6 @@ namespace RGB.NET.Devices.Debug
{
#region Properties & Fields
/// <inheritdoc />
public override DebugRGBDeviceInfo DeviceInfo { get; }
public IDeviceLayout Layout { get; }
private Action<IEnumerable<Led>>? _updateLedsAction;
@ -27,12 +24,11 @@ namespace RGB.NET.Devices.Debug
/// Internal constructor of <see cref="DebugRGBDeviceInfo"/>.
/// </summary>
internal DebugRGBDevice(IDeviceLayout layout, Action<IEnumerable<Led>>? updateLedsAction = null)
: base(new DebugRGBDeviceInfo(layout.Type, layout.Vendor ?? "RGB.NET", layout.Model ?? "Debug", layout.CustomData), new DebugDeviceUpdateQueue())
{
this.Layout = layout;
this._updateLedsAction = updateLedsAction;
DeviceInfo = new DebugRGBDeviceInfo(layout.Type, layout.Vendor ?? "RGB.NET", layout.Model ?? "Debug", layout.CustomData);
Layout.ApplyTo(this, true);
}

View File

@ -5,8 +5,6 @@ namespace RGB.NET.Devices.Logitech
/// <summary>
/// Represents a logitech RGB-device.
/// </summary>
internal interface ILogitechRGBDevice : IRGBDevice
{
void Initialize(UpdateQueue updateQueue);
}
public interface ILogitechRGBDevice : IRGBDevice
{ }
}

View File

@ -3,60 +3,21 @@
namespace RGB.NET.Devices.Logitech
{
/// <inheritdoc cref="AbstractRGBDevice{TDeviceInfo}" />
/// <inheritdoc cref="ILogitechRGBDevice" />
/// <summary>
/// Represents a generic Logitech-device. (keyboard, mouse, headset, mousepad).
/// </summary>
public abstract class LogitechRGBDevice<TDeviceInfo> : AbstractRGBDevice<TDeviceInfo>, ILogitechRGBDevice
where TDeviceInfo : LogitechRGBDeviceInfo
{
#region Properties & Fields
/// <inheritdoc />
/// <summary>
/// Gets information about the <see cref="T:RGB.NET.Devices.Logitech.LogitechRGBDevice" />.
/// </summary>
public override TDeviceInfo DeviceInfo { get; }
/// <summary>
/// Gets or sets the update queue performing updates for this device.
/// </summary>
// ReSharper disable once MemberCanBePrivate.Global
protected UpdateQueue? UpdateQueue { get; set; }
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="LogitechRGBDevice{TDeviceInfo}"/> class.
/// </summary>
/// <param name="info">The generic information provided by Logitech for the device.</param>
protected LogitechRGBDevice(TDeviceInfo info)
{
this.DeviceInfo = info;
}
#endregion
#region Methods
/// <summary>
/// Initializes the device.
/// </summary>
public virtual void Initialize(UpdateQueue updateQueue)
{
UpdateQueue = updateQueue;
}
/// <inheritdoc />
public override void Dispose()
{
try { UpdateQueue?.Dispose(); }
catch { /* at least we tried */ }
base.Dispose();
}
protected LogitechRGBDevice(TDeviceInfo info, IUpdateQueue updateQueue)
: base(info, updateQueue)
{ }
#endregion
}

View File

@ -3,8 +3,6 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using RGB.NET.Core;
using RGB.NET.Devices.Logitech.HID;
using RGB.NET.Devices.Logitech.Native;
@ -15,7 +13,7 @@ namespace RGB.NET.Devices.Logitech
/// <summary>
/// Represents a device provider responsible for logitech devices.
/// </summary>
public class LogitechDeviceProvider : IRGBDeviceProvider
public class LogitechDeviceProvider : AbstractRGBDeviceProvider
{
#region Properties & Fields
@ -37,21 +35,8 @@ namespace RGB.NET.Devices.Logitech
/// </summary>
public static List<string> PossibleX64NativePaths { get; } = new() { "x64/LogitechLedEnginesWrapper.dll" };
/// <inheritdoc />
public bool IsInitialized { get; private set; }
/// <inheritdoc />
public IEnumerable<IRGBDevice> Devices { get; private set; } = Enumerable.Empty<IRGBDevice>();
/// <summary>
/// The <see cref="DeviceUpdateTrigger"/> used to trigger the updates for logitech devices.
/// </summary>
public DeviceUpdateTrigger UpdateTrigger { get; }
// ReSharper disable once CollectionNeverQueried.Local - for now this is just to make sure they're never collected
private readonly Dictionary<RGBDeviceType, LogitechZoneUpdateQueue> _zoneUpdateQueues = new();
private LogitechPerDeviceUpdateQueue _perDeviceUpdateQueue;
private LogitechPerKeyUpdateQueue _perKeyUpdateQueue;
private LogitechPerDeviceUpdateQueue? _perDeviceUpdateQueue;
private LogitechPerKeyUpdateQueue? _perKeyUpdateQueue;
#endregion
@ -65,116 +50,54 @@ namespace RGB.NET.Devices.Logitech
{
if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(LogitechDeviceProvider)}");
_instance = this;
UpdateTrigger = new DeviceUpdateTrigger();
_perDeviceUpdateQueue = new LogitechPerDeviceUpdateQueue(UpdateTrigger);
_perKeyUpdateQueue = new LogitechPerKeyUpdateQueue(UpdateTrigger);
}
#endregion
#region Methods
/// <inheritdoc />
public bool Initialize(RGBDeviceType loadFilter = RGBDeviceType.All, bool throwExceptions = false)
protected override void InitializeSDK()
{
try
_perDeviceUpdateQueue = new LogitechPerDeviceUpdateQueue(GetUpdateTrigger());
_perKeyUpdateQueue = new LogitechPerKeyUpdateQueue(GetUpdateTrigger());
_LogitechGSDK.Reload();
if (!_LogitechGSDK.LogiLedInit()) Throw(new RGBDeviceException("Failed to initialize Logitech-SDK."));
_LogitechGSDK.LogiLedSaveCurrentLighting();
}
//TODO DarthAffe 04.03.2021: Rework device selection and configuration for HID-based providers
protected override IEnumerable<IRGBDevice> LoadDevices()
{
DeviceChecker.LoadDeviceList();
if (DeviceChecker.IsPerKeyDeviceConnected && (_perKeyUpdateQueue != null))
{
if (IsInitialized)
_LogitechGSDK.LogiLedRestoreLighting();
}
catch { /* At least we tried ... */ }
IsInitialized = false;
try
{
UpdateTrigger.Stop();
_LogitechGSDK.Reload();
if (!_LogitechGSDK.LogiLedInit()) return false;
_LogitechGSDK.LogiLedSaveCurrentLighting();
IList<IRGBDevice> devices = new List<IRGBDevice>();
DeviceChecker.LoadDeviceList();
try
{
if (DeviceChecker.IsPerKeyDeviceConnected)
{
(string model, RGBDeviceType deviceType, int _, int _) = DeviceChecker.PerKeyDeviceData;
if (loadFilter.HasFlag(deviceType)) //TODO DarthAffe 07.12.2017: Check if it's worth to try another device if the one returned doesn't match the filter
{
ILogitechRGBDevice device = new LogitechPerKeyRGBDevice(new LogitechRGBDeviceInfo(deviceType, model, LogitechDeviceCaps.PerKeyRGB, 0));
device.Initialize(_perKeyUpdateQueue);
devices.Add(device);
}
}
}
catch { if (throwExceptions) throw; }
try
{
if (DeviceChecker.IsPerDeviceDeviceConnected)
{
(string model, RGBDeviceType deviceType, int _, int _) = DeviceChecker.PerDeviceDeviceData;
if (loadFilter.HasFlag(deviceType)) //TODO DarthAffe 07.12.2017: Check if it's worth to try another device if the one returned doesn't match the filter
{
ILogitechRGBDevice device = new LogitechPerDeviceRGBDevice(new LogitechRGBDeviceInfo(deviceType, model, LogitechDeviceCaps.DeviceRGB, 0));
device.Initialize(_perDeviceUpdateQueue);
devices.Add(device);
}
}
}
catch { if (throwExceptions) throw; }
try
{
if (DeviceChecker.IsZoneDeviceConnected)
{
foreach ((string model, RGBDeviceType deviceType, int _, int zones) in DeviceChecker.ZoneDeviceData)
try
{
if (loadFilter.HasFlag(deviceType))
{
LogitechZoneUpdateQueue updateQueue = new(UpdateTrigger, deviceType);
ILogitechRGBDevice device = new LogitechZoneRGBDevice(new LogitechRGBDeviceInfo(deviceType, model, LogitechDeviceCaps.DeviceRGB, zones));
device.Initialize(updateQueue);
devices.Add(device);
_zoneUpdateQueues.Add(deviceType, updateQueue);
}
}
catch { if (throwExceptions) throw; }
}
}
catch { if (throwExceptions) throw; }
UpdateTrigger.Start();
Devices = new ReadOnlyCollection<IRGBDevice>(devices);
IsInitialized = true;
}
catch
{
if (throwExceptions)
throw;
return false;
(string model, RGBDeviceType deviceType, int _, int _) = DeviceChecker.PerKeyDeviceData;
yield return new LogitechPerKeyRGBDevice(new LogitechRGBDeviceInfo(deviceType, model, LogitechDeviceCaps.PerKeyRGB, 0), _perKeyUpdateQueue);
}
return true;
if (DeviceChecker.IsPerDeviceDeviceConnected && (_perDeviceUpdateQueue != null))
{
(string model, RGBDeviceType deviceType, int _, int _) = DeviceChecker.PerDeviceDeviceData;
yield return new LogitechPerDeviceRGBDevice(new LogitechRGBDeviceInfo(deviceType, model, LogitechDeviceCaps.DeviceRGB, 0), _perDeviceUpdateQueue);
}
if (DeviceChecker.IsZoneDeviceConnected)
{
foreach ((string model, RGBDeviceType deviceType, int _, int zones) in DeviceChecker.ZoneDeviceData)
{
LogitechZoneUpdateQueue updateQueue = new(GetUpdateTrigger(), deviceType);
yield return new LogitechZoneRGBDevice(new LogitechRGBDeviceInfo(deviceType, model, LogitechDeviceCaps.DeviceRGB, zones), updateQueue);
}
}
}
/// <inheritdoc />
public void Dispose()
public override void Dispose()
{
try { UpdateTrigger.Dispose(); }
catch { /* at least we tried */ }
foreach (IRGBDevice device in Devices)
try { device.Dispose(); }
catch { /* at least we tried */ }
Devices = Enumerable.Empty<IRGBDevice>();
base.Dispose();
try { _LogitechGSDK.LogiLedRestoreLighting(); }
catch { /* at least we tried */ }

View File

@ -17,26 +17,25 @@ namespace RGB.NET.Devices.Logitech
/// Initializes a new instance of the <see cref="T:RGB.NET.Devices.Logitech.LogitechPerDeviceRGBDevice" /> class.
/// </summary>
/// <param name="info">The specific information provided by logitech for the per-device-lightable device</param>
internal LogitechPerDeviceRGBDevice(LogitechRGBDeviceInfo info)
: base(info)
{ }
internal LogitechPerDeviceRGBDevice(LogitechRGBDeviceInfo info, IUpdateQueue updateQueue)
: base(info, updateQueue)
{
InitializeLayout();
}
#endregion
#region Methods
/// <inheritdoc />
public override void Initialize(UpdateQueue updateQueue)
private void InitializeLayout()
{
base.Initialize(updateQueue);
AddLed(LedId.Custom1, new Point(0, 0), new Size(10, 10));
}
/// <inheritdoc />
protected override object GetLedCustomData(LedId ledId) => (ledId, LogitechLedId.DEVICE);
/// <inheritdoc />
protected override void UpdateLeds(IEnumerable<Led> ledsToUpdate) => UpdateQueue?.SetData(ledsToUpdate.Where(x => x.Color.A > 0).Take(1));
protected override void UpdateLeds(IEnumerable<Led> ledsToUpdate) => UpdateQueue.SetData(GetUpdateData(ledsToUpdate.Take(1)));
#endregion
}

View File

@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using RGB.NET.Core;
using RGB.NET.Devices.Logitech.Native;
@ -27,9 +25,9 @@ namespace RGB.NET.Devices.Logitech
#region Methods
/// <inheritdoc />
protected override void Update(Dictionary<object, Color> dataSet)
protected override void Update(in ReadOnlySpan<(object key, Color color)> dataSet)
{
Color color = dataSet.Values.First();
Color color = dataSet[0].color;
_LogitechGSDK.LogiLedSetTargetDevice(LogitechDeviceCaps.DeviceRGB);
_LogitechGSDK.LogiLedSetLighting((int)Math.Round(color.R * 100),

View File

@ -1,5 +1,4 @@
using System.Collections.Generic;
using System.Linq;
using RGB.NET.Core;
namespace RGB.NET.Devices.Logitech
@ -17,8 +16,8 @@ namespace RGB.NET.Devices.Logitech
/// Initializes a new instance of the <see cref="T:RGB.NET.Devices.Logitech.LogitechPerKeyRGBDevice" /> class.
/// </summary>
/// <param name="info">The specific information provided by logitech for the per-key-lightable device</param>
internal LogitechPerKeyRGBDevice(LogitechRGBDeviceInfo info)
: base(info)
internal LogitechPerKeyRGBDevice(LogitechRGBDeviceInfo info, IUpdateQueue updateQueue)
: base(info, updateQueue)
{ }
#endregion
@ -29,7 +28,7 @@ namespace RGB.NET.Devices.Logitech
protected override object GetLedCustomData(LedId ledId) => (ledId, PerKeyIdMapping.DEFAULT.TryGetValue(ledId, out LogitechLedId logitechLedId) ? logitechLedId : LogitechLedId.Invalid);
/// <inheritdoc />
protected override void UpdateLeds(IEnumerable<Led> ledsToUpdate) => UpdateQueue?.SetData(ledsToUpdate.Where(x => x.Color.A > 0));
protected override void UpdateLeds(IEnumerable<Led> ledsToUpdate) => UpdateQueue.SetData(GetUpdateData(ledsToUpdate));
#endregion
}

Some files were not shown because too many files have changed in this diff Show More