diff --git a/RGB.NET.Brushes/Gradients/LinearGradient.cs b/RGB.NET.Brushes/Gradients/LinearGradient.cs index c8f9227..535fe5d 100644 --- a/RGB.NET.Brushes/Gradients/LinearGradient.cs +++ b/RGB.NET.Brushes/Gradients/LinearGradient.cs @@ -97,10 +97,10 @@ namespace RGB.NET.Brushes.Gradients if (!gsBefore.Offset.Equals(gsAfter.Offset)) blendFactor = ((offset - gsBefore.Offset) / (gsAfter.Offset - gsBefore.Offset)); - byte colA = (byte)(((gsAfter.Color.A - gsBefore.Color.A) * blendFactor) + gsBefore.Color.A); - byte colR = (byte)(((gsAfter.Color.R - gsBefore.Color.R) * blendFactor) + gsBefore.Color.R); - byte colG = (byte)(((gsAfter.Color.G - gsBefore.Color.G) * blendFactor) + gsBefore.Color.G); - byte colB = (byte)(((gsAfter.Color.B - gsBefore.Color.B) * blendFactor) + gsBefore.Color.B); + double colA = ((gsAfter.Color.A - gsBefore.Color.A) * blendFactor) + gsBefore.Color.A; + double colR = ((gsAfter.Color.R - gsBefore.Color.R) * blendFactor) + gsBefore.Color.R; + double colG = ((gsAfter.Color.G - gsBefore.Color.G) * blendFactor) + gsBefore.Color.G; + double colB = ((gsAfter.Color.B - gsBefore.Color.B) * blendFactor) + gsBefore.Color.B; return new Color(colA, colR, colG, colB); } diff --git a/RGB.NET.Brushes/Gradients/RainbowGradient.cs b/RGB.NET.Brushes/Gradients/RainbowGradient.cs index b99a87a..01946d0 100644 --- a/RGB.NET.Brushes/Gradients/RainbowGradient.cs +++ b/RGB.NET.Brushes/Gradients/RainbowGradient.cs @@ -73,10 +73,8 @@ namespace RGB.NET.Brushes.Gradients public Color GetColor(double offset) { double range = EndHue - StartHue; - double hue = (StartHue + (range * offset)) % 360f; - if (hue < 0) - hue += 360; - return Color.FromHSV(hue, 1, 1); + double hue = StartHue + (range * offset); + return HSVColor.Create(hue, 1, 1); } /// @@ -87,7 +85,7 @@ namespace RGB.NET.Brushes.Gradients StartHue += offset; EndHue += offset; - + while ((StartHue > 360) && (EndHue > 360)) { StartHue -= 360; diff --git a/RGB.NET.Core/Brushes/AbstractBrush.cs b/RGB.NET.Core/Brushes/AbstractBrush.cs index 716cc4a..0c6591a 100644 --- a/RGB.NET.Core/Brushes/AbstractBrush.cs +++ b/RGB.NET.Core/Brushes/AbstractBrush.cs @@ -115,7 +115,7 @@ namespace RGB.NET.Core // 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!!! - return color.MultiplyValue(Brightness.Clamp(0, 1)) + return color.MultiplyHSV(value: Brightness.Clamp(0, 1)) .MultiplyA(Opacity.Clamp(0, 1)); } diff --git a/RGB.NET.Core/Color/Behaviors/DefaultColorBehavior.cs b/RGB.NET.Core/Color/Behaviors/DefaultColorBehavior.cs new file mode 100644 index 0000000..e666f6f --- /dev/null +++ b/RGB.NET.Core/Color/Behaviors/DefaultColorBehavior.cs @@ -0,0 +1,80 @@ +namespace RGB.NET.Core +{ + public class DefaultColorBehavior : IColorBehavior + { + #region Properties & Fields + + private static DefaultColorBehavior _instance = new DefaultColorBehavior(); + /// + /// Gets the singleton instance of . + /// + public static DefaultColorBehavior Instance { get; } = _instance; + + #endregion + + #region Constructors + + private DefaultColorBehavior() + { } + + #endregion + + #region Methods + + /// + /// Converts the individual byte values of this to a human-readable string. + /// + /// A string that contains the individual byte values of this . For example "[A: 255, R: 255, G: 0, B: 0]". + public virtual string ToString(Color color) => $"[A: {color.GetA()}, R: {color.GetR()}, G: {color.GetG()}, B: {color.GetB()}]"; + + /// + /// Tests whether the specified object is a and is equivalent to this . + /// + /// The object to test. + /// true if is a equivalent to this ; otherwise, false. + public virtual bool Equals(Color color, object obj) + { + if (!(obj is Color)) return false; + + (double a, double r, double g, double b) = ((Color)obj).GetRGB(); + return color.A.EqualsInTolerance(a) && color.R.EqualsInTolerance(r) && color.G.EqualsInTolerance(g) && color.B.EqualsInTolerance(b); + } + + /// + /// Returns a hash code for this . + /// + /// An integer value that specifies the hash code for this . + public virtual int GetHashCode(Color color) + { + unchecked + { + int hashCode = color.A.GetHashCode(); + hashCode = (hashCode * 397) ^ color.R.GetHashCode(); + hashCode = (hashCode * 397) ^ color.G.GetHashCode(); + hashCode = (hashCode * 397) ^ color.B.GetHashCode(); + return hashCode; + } + } + + /// + /// Blends a over this color. + /// + /// The to blend. + public virtual Color Blend(Color baseColor, 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)); + + return new Color(resultA, resultR, resultG, resultB); + } + + #endregion + } +} diff --git a/RGB.NET.Core/Color/Behaviors/IColorBehavior.cs b/RGB.NET.Core/Color/Behaviors/IColorBehavior.cs new file mode 100644 index 0000000..630aca5 --- /dev/null +++ b/RGB.NET.Core/Color/Behaviors/IColorBehavior.cs @@ -0,0 +1,13 @@ +namespace RGB.NET.Core +{ + public interface IColorBehavior + { + string ToString(Color color); + + bool Equals(Color color, object obj); + + int GetHashCode(Color color); + + Color Blend(Color baseColor, Color blendColor); + } +} diff --git a/RGB.NET.Core/Color/Color.cs b/RGB.NET.Core/Color/Color.cs new file mode 100644 index 0000000..81a3a33 --- /dev/null +++ b/RGB.NET.Core/Color/Color.cs @@ -0,0 +1,288 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable UnusedMethodReturnValue.Global + +using System; +using System.Diagnostics; + +namespace RGB.NET.Core +{ + /// + /// + /// Represents an ARGB (alpha, red, green, blue) color. + /// + [DebuggerDisplay("[A: {A}, R: {R}, G: {G}, B: {B}]")] + public struct Color + { + #region Constants + + /// + /// Gets an transparent color [A: 0, R: 0, G: 0, B: 0] + /// + public static Color Transparent => new Color(0, 0, 0, 0); + + #endregion + + #region Properties & Fields + + private static IColorBehavior _behavior = DefaultColorBehavior.Instance; + /// + /// Gets or sets the used to perform operations on colors. + /// + public static IColorBehavior Behavior + { + get => _behavior; + set => _behavior = value ?? DefaultColorBehavior.Instance; + } + + /// + /// Gets the alpha component value of this as percentage in the range [0..1]. + /// + public double A { get; } + + /// + /// Gets the red component value of this as percentage in the range [0..1]. + /// + public double R { get; } + + /// + /// Gets the green component value of this as percentage in the range [0..1]. + /// + public double G { get; } + + /// + /// Gets the blue component value of this as percentage in the range [0..1]. + /// + public double B { get; } + + #endregion + + #region Constructors + + /// + /// + /// Initializes a new instance of the struct using RGB-Values. + /// Alpha defaults to 255. + /// + /// The red component value of this . + /// The green component value of this . + /// The blue component value of this . + public Color(byte r, byte g, byte b) + : this(byte.MaxValue, r, g, b) + { } + + /// + /// + /// Initializes a new instance of the struct using RGB-Values. + /// Alpha defaults to 255. + /// + /// The red component value of this . + /// The green component value of this . + /// The blue component value of this . + public Color(int r, int g, int b) + : this((byte)r.Clamp(0, byte.MaxValue), (byte)g.Clamp(0, byte.MaxValue), (byte)b.Clamp(0, byte.MaxValue)) + { } + + /// + /// Initializes a new instance of the struct using ARGB values. + /// + /// The alpha component value of this . + /// The red component value of this . + /// The green component value of this . + /// The blue component value of this . + public Color(byte a, byte r, byte g, byte b) + : this(a.GetPercentageFromByteValue(), r.GetPercentageFromByteValue(), g.GetPercentageFromByteValue(), b.GetPercentageFromByteValue()) + { } + + /// + /// Initializes a new instance of the struct using ARGB values. + /// + /// The alpha component value of this . + /// The red component value of this . + /// The green component value of this . + /// The blue component value of this . + public Color(int a, int r, int g, int b) + : this((byte)a.Clamp(0, byte.MaxValue), (byte)r.Clamp(0, byte.MaxValue), (byte)g.Clamp(0, byte.MaxValue), (byte)b.Clamp(0, byte.MaxValue)) + { } + + /// + /// Initializes a new instance of the struct using RGB-percent values. + /// Alpha defaults to 1.0. + /// + /// The red component value of this . + /// The green component value of this . + /// The blue component value of this . + public Color(double r, double g, double b) + : this(1.0, r, g, b) + { } + + /// + /// Initializes a new instance of the struct using ARGB-percent values. + /// + /// The alpha component value of this . + /// The red component value of this . + /// The green component value of this . + /// The blue component value of this . + public Color(double a, byte r, byte g, byte b) + : this(a, r.GetPercentageFromByteValue(), g.GetPercentageFromByteValue(), b.GetPercentageFromByteValue()) + { } + + /// + /// Initializes a new instance of the struct using ARGB-percent values. + /// + /// The alpha component value of this . + /// The red component value of this . + /// The green component value of this . + /// The blue component value of this . + public Color(double 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)) + { } + + /// + /// Initializes a new instance of the struct using ARGB-percent values. + /// + /// The alpha component value of this . + /// The red component value of this . + /// The green component value of this . + /// The blue component value of this . + public Color(int a, double r, double g, double b) + : this((byte)a.Clamp(0, byte.MaxValue), r, g, b) + { } + + /// + /// Initializes a new instance of the struct using ARGB-percent values. + /// + /// The alpha component value of this . + /// The red component value of this . + /// The green component value of this . + /// The blue component value of this . + public Color(byte a, double r, double g, double b) + : this(a.GetPercentageFromByteValue(), r, g, b) + { } + + /// + /// Initializes a new instance of the struct using ARGB-percent values. + /// + /// The alpha component value of this . + /// The red component value of this . + /// The green component value of this . + /// The blue component value of this . + public Color(double a, double r, double g, double b) + { + A = a.Clamp(0, 1); + R = r.Clamp(0, 1); + G = g.Clamp(0, 1); + B = b.Clamp(0, 1); + } + + /// + /// + /// Initializes a new instance of the struct by cloning a existing . + /// + /// The the values are copied from. + public Color(Color color) + : this(color.A, color.R, color.G, color.B) + { } + + #endregion + + #region Methods + + /// + /// Gets a human-readable string, as defined by the current . + /// + /// A string that contains the individual byte values of this . Default format: "[A: 255, R: 255, G: 0, B: 0]". + public override string ToString() => Behavior.ToString(this); + + /// + /// Tests whether the specified object is a and is equivalent to this , as defined by the current . + /// + /// The object to test. + /// true if is a equivalent to this ; otherwise, false. + public override bool Equals(object obj) => Behavior.Equals(this, obj); + + /// + /// Returns a hash code for this , as defined by the current . + /// + /// An integer value that specifies the hash code for this . + public override int GetHashCode() => Behavior.GetHashCode(this); + + /// + /// Blends a over this color, as defined by the current . + /// + /// The to blend. + public Color Blend(Color color) => Behavior.Blend(this, color); + + #endregion + + #region Operators + + /// + /// Blends the provided colors as if would've been called on . + /// + /// The base color. + /// The color to blend. + /// The blended color. + public static Color operator +(Color color1, Color color2) => color1.Blend(color2); + + /// + /// Returns a value that indicates whether two specified are equal. + /// + /// The first to compare. + /// The second to compare. + /// true if and are equal; otherwise, false. + public static bool operator ==(Color color1, Color color2) => color1.Equals(color2); + + /// + /// Returns a value that indicates whether two specified are equal. + /// + /// The first to compare. + /// The second to compare. + /// true if and are not equal; otherwise, false. + public static bool operator !=(Color color1, Color color2) => !(color1 == color2); + + /// + /// Converts a of ARGB-components to a . + /// + /// The containing the components. + /// The color. + public static implicit operator Color((byte r, byte g, byte b) components) => new Color(components.r, components.g, components.b); + + /// + /// Converts a of ARGB-components to a . + /// + /// The containing the components. + /// The color. + public static implicit operator Color((byte a, byte r, byte g, byte b) components) => new Color(components.a, components.r, components.g, components.b); + + /// + /// Converts a of ARGB-components to a . + /// + /// The containing the components. + /// The color. + public static implicit operator Color((int r, int g, int b) components) => new Color(components.r, components.g, components.b); + + /// + /// Converts a of ARGB-components to a . + /// + /// The containing the components. + /// The color. + public static implicit operator Color((int a, int r, int g, int b) components) => new Color(components.a, components.r, components.g, components.b); + + /// + /// Converts a of ARGB-components to a . + /// + /// The containing the components. + /// The color. + public static implicit operator Color((double r, double g, double b) components) => new Color(components.r, components.g, components.b); + + /// + /// Converts a of ARGB-components to a . + /// + /// The containing the components. + /// The color. + public static implicit operator Color((double a, double r, double g, double b) components) => new Color(components.a, components.r, components.g, components.b); + + #endregion + } +} diff --git a/RGB.NET.Core/Color/HSVColor.cs b/RGB.NET.Core/Color/HSVColor.cs new file mode 100644 index 0000000..f99581c --- /dev/null +++ b/RGB.NET.Core/Color/HSVColor.cs @@ -0,0 +1,227 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global +using System; + +namespace RGB.NET.Core +{ + public static class HSVColor + { + #region Getter + + /// + /// Gets the hue component value (HSV-color space) of this as degree in the range [0..360]. + /// + /// + /// + public static double GetHue(this Color color) => color.GetHSV().hue; + + /// + /// Gets the saturation component value (HSV-color space) of this in the range [0..1]. + /// + /// + /// + public static double GetSaturation(this Color color) => color.GetHSV().saturation; + + /// + /// Gets the value component value (HSV-color space) of this in the range [0..1]. + /// + /// + /// + public static double GetValue(this Color color) => color.GetHSV().value; + + /// + /// Gets the hue, saturation and value component values (HSV-color space) of this . + /// Hue as degree in the range [0..1]. + /// Saturation in the range [0..1]. + /// Value in the range [0..1]. + /// + /// + /// + public static (double hue, double saturation, double value) GetHSV(this Color color) + => CaclulateHSVFromRGB(color.R, color.G, color.B); + + #endregion + + #region Manipulation + + /// + /// Adds the given HSV values to this color. + /// + /// The hue value to add. + /// The saturation value to add. + /// The value value to add. + /// The new color after the modification. + public static Color AddHSV(this Color color, double hue = 0, double saturation = 0, double value = 0) + { + (double cHue, double cSaturation, double cValue) = color.GetHSV(); + return Create(color.A, cHue + hue, cSaturation + saturation, cValue + value); + } + + /// + /// Subtracts the given HSV values to this color. + /// + /// The hue value to subtract. + /// The saturation value to subtract. + /// The value value to subtract. + /// The new color after the modification. + public static Color SubtractHSV(this Color color, double hue = 0, double saturation = 0, double value = 0) + { + (double cHue, double cSaturation, double cValue) = color.GetHSV(); + return Create(color.A, cHue - hue, cSaturation - saturation, cValue - value); + } + + /// + /// Multiplies the given HSV values to this color. + /// + /// The hue value to multiply. + /// The saturation value to multiply. + /// The value value to multiply. + /// The new color after the modification. + public static Color MultiplyHSV(this Color color, double hue = 1, double saturation = 1, double value = 1) + { + (double cHue, double cSaturation, double cValue) = color.GetHSV(); + return Create(color.A, cHue * hue, cSaturation * saturation, cValue * value); + } + + /// + /// Divides the given HSV values to this color. + /// + /// The hue value to divide. + /// The saturation value to divide. + /// The value value to divide. + /// The new color after the modification. + public static Color DivideHSV(this Color color, double hue = 1, double saturation = 1, double value = 1) + { + (double cHue, double cSaturation, double cValue) = color.GetHSV(); + return Create(color.A, cHue / hue, cSaturation / saturation, cValue / value); + } + + /// + /// Sets the given hue value of this color. + /// + /// The hue value to set. + /// The saturation value to set. + /// The value value to set. + /// The new color after the modification. + public static Color SetHSV(this Color color, double? hue = null, double? saturation = null, double? value = null) + { + (double cHue, double cSaturation, double cValue) = color.GetHSV(); + return Create(color.A, hue ?? cHue, saturation ?? cSaturation, value ?? cValue); + } + + #endregion + + #region Factory + + /// + /// Creates a new instance of the struct using HSV-Values. + /// + /// The hue component value of this . + /// The saturation component value of this . + /// The value component value of this . + /// The color created from the values. + public static Color Create(double hue, double saturation, double value) + => Create(1.0, hue, saturation, value); + + /// + /// Creates a new instance of the struct using AHSV-Values. + /// + /// The alpha component value of this . + /// The hue component value of this . + /// The saturation component value of this . + /// The value component value of this . + /// The color created from the values. + public static Color Create(byte a, double hue, double saturation, double value) + => Create((double)a / byte.MaxValue, hue, saturation, value); + + /// + /// Creates a new instance of the struct using AHSV-Values. + /// + /// The alpha component value of this . + /// The hue component value of this . + /// The saturation component value of this . + /// The value component value of this . + /// The color created from the values. + public static Color Create(int a, double hue, double saturation, double value) + => Create((double)a / byte.MaxValue, hue, saturation, value); + + /// + /// Creates a new instance of the struct using AHSV-Values. + /// + /// The alpha component value of this . + /// The hue component value of this . + /// The saturation component value of this . + /// The value component value of this . + /// The color created from the values. + public static Color Create(double a, double hue, double saturation, double value) + { + (double r, double g, double b) = CalculateRGBFromHSV(hue, saturation, value); + return new Color(a, r, g, b); + } + + #endregion + + #region Helper + + private static (double h, double s, double v) CaclulateHSVFromRGB(double r, double g, double 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); + + double 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)); + else // b is max + hue = 4.0 + ((r - g) / (max - min)); + + hue = hue * 60.0; + hue = hue.Wrap(0, 360); + + double saturation = max.EqualsInTolerance(0) ? 0 : 1.0 - (min / max); + double 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) + { + h = h.Wrap(0, 360); + s = s.Clamp(0, 1); + v = v.Clamp(0, 1); + + if (s <= 0.0) + return (v, v, v); + + double hh = h / 60.0; + 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))); + + switch (i) + { + case 0: + return (v, t, p); + case 1: + return (q, v, p); + case 2: + return (p, v, t); + case 3: + return (p, q, v); + case 4: + return (t, p, v); + default: + return (v, p, q); + } + } + + #endregion + } +} diff --git a/RGB.NET.Core/Color/RGBColor.cs b/RGB.NET.Core/Color/RGBColor.cs new file mode 100644 index 0000000..b196076 --- /dev/null +++ b/RGB.NET.Core/Color/RGBColor.cs @@ -0,0 +1,275 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global +using System; + +namespace RGB.NET.Core +{ + public static class RGBColor + { + #region Getter + + /// + /// Gets the A component value of this as byte in the range [0..255]. + /// + /// + /// + public static byte GetA(this Color color) => color.A.GetByteValueFromPercentage(); + + /// + /// Gets the R component value of this as byte in the range [0..255]. + /// + /// + /// + public static byte GetR(this Color color) => color.R.GetByteValueFromPercentage(); + + /// + /// Gets the G component value of this as byte in the range [0..255]. + /// + /// + /// + public static byte GetG(this Color color) => color.G.GetByteValueFromPercentage(); + + /// + /// Gets the B component value of this as byte in the range [0..255]. + /// + /// + /// + public static byte GetB(this Color color) => color.B.GetByteValueFromPercentage(); + + /// + /// Gets the A, R, G and B component value of this as byte in the range [0..255]. + /// + /// + /// + public static (byte a, byte r, byte g, byte b) GetRGBBytes(this Color color) + => (color.GetA(), color.GetR(), color.GetG(), color.GetB()); + + /// + /// Gets the A, R, G and B component value of this as percentage in the range [0..1]. + /// + /// + /// + public static (double a, double r, double g, double b) GetRGB(this Color color) + => (color.A, color.R, color.G, color.B); + + #endregion + + #region Manipulation + + #region Add + + /// + /// Adds the given RGB values to this color. + /// + /// The red value to add. + /// The green value to add. + /// The blue value to add. + /// The new color after the modification. + public static Color AddRGB(this Color color, int r = 0, int g = 0, int b = 0) + => new Color(color.A, color.GetR() + r, color.GetG() + g, color.GetB() + b); + + /// + /// Adds the given RGB-percent values to this color. + /// + /// The red value to add. + /// The green value to add. + /// The blue value to add. + /// The new color after the modification. + public static Color AddRGB(this Color color, double r = 0, double g = 0, double b = 0) + => new Color(color.A, color.R + r, color.G + g, color.B + b); + + /// + /// Adds the given alpha value to this color. + /// + /// The alpha value to add. + /// The new color after the modification. + public static Color AddA(this Color color, int a) + => new Color(color.GetA() + a, color.R, color.G, color.B); + + /// + /// Adds the given alpha-percent value to this color. + /// + /// The alpha value to add. + /// The new color after the modification. + public static Color AddA(this Color color, double a) + => new Color(color.A + a, color.R, color.G, color.B); + + #endregion + + #region Subtract + + /// + /// Subtracts the given RGB values to this color. + /// + /// The red value to subtract. + /// The green value to subtract. + /// The blue value to subtract. + /// The new color after the modification. + public static Color SubtractRGB(this Color color, int r = 0, int g = 0, int b = 0) + => new Color(color.A, color.GetR() - r, color.GetG() - g, color.GetB() - b); + + /// + /// Subtracts the given RGB values to this color. + /// + /// The red value to subtract. + /// The green value to subtract. + /// The blue value to subtract. + /// The new color after the modification. + public static Color SubtractRGB(this Color color, double r = 0, double g = 0, double b = 0) + => new Color(color.A, color.R - r, color.G - g, color.B - b); + + /// + /// Subtracts the given alpha value to this color. + /// + /// The alpha value to subtract. + /// The new color after the modification. + public static Color SubtractA(this Color color, int a) + => new Color(color.GetA() - a, color.R, color.G, color.B); + + /// + /// Subtracts the given alpha-percent value to this color. + /// + /// The alpha value to subtract. + /// The new color after the modification. + public static Color SubtractA(this Color color, double aPercent) + => new Color(color.A - aPercent, color.R, color.G, color.B); + + #endregion + + #region Multiply + + /// + /// Multiplies the given RGB values to this color. + /// + /// The red value to multiply. + /// The green value to multiply. + /// The blue value to multiply. + /// The new color after the modification. + public static Color MultiplyRGB(this Color color, double r = 1, double g = 1, double b = 1) + => new Color(color.A, color.R * r, color.G * g, color.B * b); + + /// + /// Multiplies the given alpha value to this color. + /// + /// The alpha value to multiply. + /// The new color after the modification. + public static Color MultiplyA(this Color color, double a) + => new Color(color.A * a, color.R, color.G, color.B); + + #endregion + + #region Divide + + /// + /// Divides the given RGB values to this color. + /// + /// The red value to divide. + /// The green value to divide. + /// The blue value to divide. + /// The new color after the modification. + public static Color DivideRGB(this Color color, double r = 1, double g = 1, double b = 1) + => new Color(color.A, color.R / r, color.G / g, color.B / b); + + /// + /// Divides the given alpha value to this color. + /// + /// The alpha value to divide. + /// The new color after the modification. + public static Color DivideA(this Color color, double a) + => new Color(color.A / a, color.R, color.G, color.B); + + #endregion + + #region Set + + /// + /// Sets the given RGB value of this color. + /// + /// The red value to set. + /// The green value to set. + /// The blue value to set. + /// The new color after the modification. + public static Color SetRGB(this Color color, byte? r = null, byte? g = null, byte? b = null) + => new Color(color.A, r ?? color.GetR(), g ?? color.GetG(), b ?? color.GetB()); + + /// + /// Sets the given RGB value of this color. + /// + /// The red value to set. + /// The green value to set. + /// The blue value to set. + /// The new color after the modification. + public static Color SetRGB(this Color color, int? r = null, int? g = null, int? b = null) + => new Color(color.A, r ?? color.GetR(), g ?? color.GetG(), b ?? color.GetB()); + + /// + /// Sets the given RGB value of this color. + /// + /// The red value to set. + /// The green value to set. + /// The blue value to set. + /// The new color after the modification. + public static Color SetRGB(this Color color, double? r = null, double? g = null, double? b = null) + => new Color(color.A, r ?? color.R, g ?? color.G, b ?? color.B); + + /// + /// Sets the given alpha value of this color. + /// + /// The alpha value to set. + /// The new color after the modification. + public static Color SetA(this Color color, int a) => new Color(a, color.R, color.G, color.B); + + /// + /// Sets the given alpha value of this color. + /// + /// The alpha value to set. + /// The new color after the modification. + public static Color SetA(this Color color, double a) => new Color(a, color.R, color.G, color.B); + + #endregion + + #endregion + + #region Conversion + + /// + /// Gets the current color as a RGB-HEX-string. + /// + /// The RGB-HEX-string. + public static string AsRGBHexString(this Color color, bool leadingHash = true) => (leadingHash ? "#" : "") + ConversionHelper.ToHex(color.GetR(), color.GetG(), color.GetB()); + + /// + /// Gets the current color as a ARGB-HEX-string. + /// + /// The ARGB-HEX-string. + public static string AsARGBHexString(this Color color, bool leadingHash = true) => (leadingHash ? "#" : "") + ConversionHelper.ToHex(color.GetA(), color.GetR(), color.GetG(), color.GetB()); + + #endregion + + #region Factory + + /// + /// Creates a new instance of the struct using a HEX-string. + /// + /// The HEX-representation of the color. + /// The color created from the HEX-string. + public static Color FromHexString(string hexString) + { + if ((hexString == null) || (hexString.Length < 6)) + throw new ArgumentException("Invalid hex string", nameof(hexString)); + + if (hexString[0] == '#') + hexString = hexString.Substring(1); + + byte[] data = ConversionHelper.HexToBytes(hexString); + if (data.Length == 3) + return new Color(data[0], data[1], data[2]); + if (data.Length == 4) + return new Color(data[0], data[1], data[2], data[3]); + + throw new ArgumentException("Invalid hex string", nameof(hexString)); + } + + #endregion + } +} diff --git a/RGB.NET.Core/Extensions/MathExtensions.cs b/RGB.NET.Core/Extensions/MathExtensions.cs index 415a53c..0a9fb88 100644 --- a/RGB.NET.Core/Extensions/MathExtensions.cs +++ b/RGB.NET.Core/Extensions/MathExtensions.cs @@ -36,7 +36,14 @@ namespace RGB.NET.Core /// The higher value of the range the value is clamped to. /// The clamped value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double Clamp(this double value, double min, double max) => Math.Max(min, Math.Min(max, value)); + public static double Clamp(this double value, double min, double max) + { + // ReSharper disable ConvertIfStatementToReturnStatement - I'm not sure why, but inlining this statement reduces performance by ~10% + if (value < min) return min; + if (value > max) return max; + return value; + // ReSharper restore ConvertIfStatementToReturnStatement + } /// /// Clamps the provided value to be bigger or equal min and smaller or equal max. @@ -46,7 +53,14 @@ namespace RGB.NET.Core /// The higher value of the range the value is clamped to. /// The clamped value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Clamp(this int value, int min, int max) => Math.Max(min, Math.Min(max, value)); + public static int Clamp(this int value, int min, int max) + { + // ReSharper disable ConvertIfStatementToReturnStatement - I'm not sure why, but inlining this statement reduces performance by ~10% + if (value < min) return min; + if (value > max) return max; + return value; + // ReSharper restore ConvertIfStatementToReturnStatement + } /// /// Enforces the provided value to be in the specified range by wrapping it around the edges if it exceeds them. @@ -68,16 +82,20 @@ namespace RGB.NET.Core return value; } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte GetByteValueFromPercentage(this double percentage) { if (double.IsNaN(percentage)) return 0; percentage = percentage.Clamp(0, 1.0); - return (byte)(percentage.Equals(1.0) ? 255 : percentage * 256.0); + return (byte)(percentage >= 1.0 ? 255 : percentage * 256.0); } - + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double GetPercentageFromByteValue(this byte value) + => ((double)value) / byte.MaxValue; + #endregion } } diff --git a/RGB.NET.Core/Leds/Color.cs b/RGB.NET.Core/Leds/Color.cs deleted file mode 100644 index cc2208d..0000000 --- a/RGB.NET.Core/Leds/Color.cs +++ /dev/null @@ -1,1124 +0,0 @@ -// ReSharper disable MemberCanBePrivate.Global -// ReSharper disable UnusedMember.Global -// ReSharper disable UnusedMethodReturnValue.Global - -using System; -using System.Diagnostics; - -namespace RGB.NET.Core -{ - /// - /// - /// Represents an ARGB (alpha, red, green, blue) color. - /// - [DebuggerDisplay("[A: {A}, R: {R}, G: {G}, B: {B}, H: {Hue}, S: {Saturation}, V: {Value}]")] - public struct Color - { - #region Constants - - /// - /// Gets an transparent color [A: 0, R: 0, G: 0, B: 0] - /// - public static Color Transparent => new Color(0, 0, 0, 0); - - #endregion - - #region Properties & Fields - - #region RGB - - /// - /// Gets the alpha component value of this as byte in the range [0..255]. - /// - public byte A { get; } - - /// - /// Gets the alpha component value of this as percentage in the range [0..1]. - /// - public double APercent => A / (double)byte.MaxValue; - - /// - /// Gets the red component value of this as byte in the range [0..255]. - /// - public byte R { get; } - - /// - /// Gets the red component value of this as percentage in the range [0..1]. - /// - public double RPercent => R / (double)byte.MaxValue; - - /// - /// Gets the green component value of this as byte in the range [0..255]. - /// - public byte G { get; } - - /// - /// Gets the green component value of this as percentage in the range [0..1]. - /// - public double GPercent => G / (double)byte.MaxValue; - - /// - /// Gets the blue component value of this as byte in the range [0..255]. - /// - public byte B { get; } - - /// - /// Gets the blue component value of this as percentage in the range [0..1]. - /// - public double BPercent => B / (double)byte.MaxValue; - - #endregion - - #region HSV - - /// - /// Gets the hue component value (HSV-color space) of this as degree in the range [0..360]. - /// - public double Hue { get; } - - /// - /// Gets the saturation component value (HSV-color space) of this as degree in the range [0..1]. - /// - public double Saturation { get; } - - /// - /// Gets the value component value (HSV-color space) of this as degree in the range [0..1]. - /// - public double Value { get; } - - #endregion - - #endregion - - #region Constructors - - /// - /// - /// Initializes a new instance of the struct using RGB-Values. - /// Alpha defaults to 255. - /// - /// The red component value of this . - /// The green component value of this . - /// The blue component value of this . - public Color(byte r, byte g, byte b) - : this(byte.MaxValue, r, g, b) - { } - - /// - /// - /// Initializes a new instance of the struct using RGB-Values. - /// Alpha defaults to 255. - /// - /// The red component value of this . - /// The green component value of this . - /// The blue component value of this . - public Color(int r, int g, int b) - : this((byte)r.Clamp(0, byte.MaxValue), (byte)g.Clamp(0, byte.MaxValue), (byte)b.Clamp(0, byte.MaxValue)) - { } - - /// - /// Initializes a new instance of the struct using ARGB values. - /// - /// The alpha component value of this . - /// The red component value of this . - /// The green component value of this . - /// The blue component value of this . - public Color(byte a, byte r, byte g, byte b) - { - this.A = a; - this.R = r; - this.G = g; - this.B = b; - - (double h, double s, double v) = CaclulateHSVFromRGB(r, g, b); - Hue = h; - Saturation = s; - Value = v; - } - - /// - /// Initializes a new instance of the struct using ARGB values. - /// - /// The alpha component value of this . - /// The red component value of this . - /// The green component value of this . - /// The blue component value of this . - public Color(int a, int r, int g, int b) - : this((byte)a.Clamp(0, byte.MaxValue), (byte)r.Clamp(0, byte.MaxValue), (byte)g.Clamp(0, byte.MaxValue), (byte)b.Clamp(0, byte.MaxValue)) - { } - - /// - /// Initializes a new instance of the struct using RGB-percent values. - /// - /// The red component value of this . - /// The green component value of this . - /// The blue component value of this . - public Color(double r, double g, double b) - : this(byte.MaxValue, - r.GetByteValueFromPercentage(), - g.GetByteValueFromPercentage(), - b.GetByteValueFromPercentage()) - { } - - /// - /// Initializes a new instance of the struct using ARGB-percent values. - /// - /// The alpha component value of this . - /// The red component value of this . - /// The green component value of this . - /// The blue component value of this . - public Color(double a, double r, double g, double b) - : this(a.GetByteValueFromPercentage(), - r.GetByteValueFromPercentage(), - g.GetByteValueFromPercentage(), - b.GetByteValueFromPercentage()) - { } - - /// - /// - /// Initializes a new instance of the struct by cloning a existing . - /// - /// The the values are copied from. - public Color(Color color) - : this(color.A, color.R, color.G, color.B) { } - - #endregion - - #region Methods - - #region Factory - - /// - /// Creates a new instance of the struct using HSV-Values. - /// - /// The hue component value of this . - /// The saturation component value of this . - /// The value component value of this . - /// The color created from the values. - public static Color FromHSV(double hue, double saturation, double value) - { - (byte r, byte g, byte b) = CalculateRGBFromHSV(hue, saturation, value); - return new Color(r, g, b); - } - - /// - /// Creates a new instance of the struct using AHSV-Values. - /// - /// The alpha component value of this . - /// The hue component value of this . - /// The saturation component value of this . - /// The value component value of this . - /// The color created from the values. - public static Color FromHSV(int a, double hue, double saturation, double value) - { - (byte r, byte g, byte b) = CalculateRGBFromHSV(hue, saturation, value); - return new Color(a, r, g, b); - } - - /// - /// Creates a new instance of the struct using a HEX-string. - /// - /// The HEX-representation of the color. - /// The color created from the HEX-string. - public static Color FromHexString(string hexString) - { - if ((hexString == null) || (hexString.Length < 6)) - throw new ArgumentException("Invalid hex string", nameof(hexString)); - - if (hexString[0] == '#') - hexString = hexString.Substring(1); - - byte[] data = ConversionHelper.HexToBytes(hexString); - if (data.Length == 3) - return new Color(data[0], data[1], data[2]); - if (data.Length == 4) - return new Color(data[0], data[1], data[2], data[3]); - - throw new ArgumentException("Invalid hex string", nameof(hexString)); - } - - #endregion - - private static (double h, double s, double v) CaclulateHSVFromRGB(byte r, byte g, byte b) - { - if ((r == g) && (g == b)) return (0, 0, r / 255.0); - - int min = Math.Min(Math.Min(r, g), b); - int max = Math.Max(Math.Max(r, g), b); - - double hue; - if (max == min) - hue = 0; - else if (max == r) // r is max - hue = (g - b) / (double)(max - min); - else if (max == g) // g is max - hue = 2.0 + ((b - r) / (double)(max - min)); - else // b is max - hue = 4.0 + ((r - g) / (double)(max - min)); - - hue = hue * 60.0; - if (hue < 0.0) - hue += 360.0; - - double saturation = (max == 0) ? 0 : 1.0 - (min / (double)max); - double value = Math.Max(r, Math.Max(g, b)) / 255.0; - - return (hue, saturation, value); - } - - private static (byte r, byte g, byte b) CalculateRGBFromHSV(double h, double s, double v) - { - h = h.Wrap(0, 360); - s = s.Clamp(0, 1); - v = v.Clamp(0, 1); - - if (s <= 0.0) - { - byte val = v.GetByteValueFromPercentage(); - return (val, val, val); - } - - double hh = h / 60.0; - 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))); - - switch (i) - { - case 0: - return (v.GetByteValueFromPercentage(), - t.GetByteValueFromPercentage(), - p.GetByteValueFromPercentage()); - case 1: - return (q.GetByteValueFromPercentage(), - v.GetByteValueFromPercentage(), - p.GetByteValueFromPercentage()); - case 2: - return (p.GetByteValueFromPercentage(), - v.GetByteValueFromPercentage(), - t.GetByteValueFromPercentage()); - case 3: - return (p.GetByteValueFromPercentage(), - q.GetByteValueFromPercentage(), - v.GetByteValueFromPercentage()); - case 4: - return (t.GetByteValueFromPercentage(), - p.GetByteValueFromPercentage(), - v.GetByteValueFromPercentage()); - default: - return (v.GetByteValueFromPercentage(), - p.GetByteValueFromPercentage(), - q.GetByteValueFromPercentage()); - } - } - - /// - /// Converts the individual byte values of this to a human-readable string. - /// - /// A string that contains the individual byte values of this . For example "[A: 255, R: 255, G: 0, B: 0]". - public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}, H: {Hue}, S: {Saturation}, V: {Value}]"; - - /// - /// Tests whether the specified object is a and is equivalent to this . - /// - /// The object to test. - /// true if is a equivalent to this ; otherwise, false. - public override bool Equals(object obj) - { - if (!(obj is Color)) return false; - - Color compareColor = (Color)obj; - return (compareColor.A == A) && (compareColor.R == R) && (compareColor.G == G) && (compareColor.B == B); - } - - /// - /// Returns a hash code for this . - /// - /// An integer value that specifies the hash code for this . - public override int GetHashCode() - { - unchecked - { - int hashCode = A.GetHashCode(); - hashCode = (hashCode * 397) ^ R.GetHashCode(); - hashCode = (hashCode * 397) ^ G.GetHashCode(); - hashCode = (hashCode * 397) ^ B.GetHashCode(); - return hashCode; - } - } - - /// - /// Gets the current color as a RGB-HEX-string. - /// - /// The RGB-HEX-string. - public string AsRGBHexString() => ConversionHelper.ToHex(R, G, B); - - /// - /// Gets the current color as a ARGB-HEX-string. - /// - /// The ARGB-HEX-string. - public string AsARGBHexString() => ConversionHelper.ToHex(A, R, G, B); - - #region Deconstruction - - /// - /// Deconstructs the Color into it's components. - /// - /// The alpha component of this color. - /// The red component of this color. - /// The green component of this color. - /// The blue component of this color. - /// The hue component of this color. - /// The saturation component of this color. - /// The value component of this color. - public void Deconstruct(out byte a, out byte r, out byte g, out byte b, out double hue, out double saturation, out double value) - { - Deconstruct(out a, out r, out g, out b); - Deconstruct(out hue, out saturation, out value); - } - - /// - /// Deconstructs the Color into it's ARGB-components. - /// - /// The alpha component of this color. - /// The red component of this color. - /// The green component of this color. - /// The blue component of this color. - public void Deconstruct(out byte a, out byte r, out byte g, out byte b) - { - a = A; - r = R; - g = G; - b = B; - } - - /// - /// Deconstructs the Color into it's HSV-components. - /// - /// The hue component of this color. - /// The saturation component of this color. - /// The value component of this color. - public void Deconstruct(out double hue, out double saturation, out double value) - { - hue = Hue; - saturation = Saturation; - value = Value; - } - - #endregion - - #region Manipulation - - /// - /// Blends a over this color. - /// - /// The to blend. - public Color Blend(Color color) - { - if (color.A == 0) return this; - - if (color.A == 255) - return color; - - double resultA = (1.0 - ((1.0 - color.APercent) * (1.0 - APercent))); - double resultR = (((color.RPercent * color.APercent) / resultA) + ((RPercent * APercent * (1.0 - color.APercent)) / resultA)); - double resultG = (((color.GPercent * color.APercent) / resultA) + ((GPercent * APercent * (1.0 - color.APercent)) / resultA)); - double resultB = (((color.BPercent * color.APercent) / resultA) + ((BPercent * APercent * (1.0 - color.APercent)) / resultA)); - - return new Color(resultA, resultR, resultG, resultB); - } - - #region Add - - /// - /// Adds the given RGB values to this color. - /// - /// The red value to add. - /// The green value to add. - /// The blue value to add. - /// The new color after the modification. - public Color AddRGB(int r, int g, int b) => AddRGB(0, r, g, b); - - /// - /// Adds the given RGB values to this color. - /// - /// The alpha value to add. - /// The red value to add. - /// The green value to add. - /// The blue value to add. - /// The new color after the modification. - public Color AddRGB(int a, int r, int g, int b) - => new Color(A + a, R + r, G + g, B + b); - - /// - /// Adds the given RGB-percent values to this color. - /// - /// The red value to add. - /// The green value to add. - /// The blue value to add. - /// The new color after the modification. - public Color AddPercent(double rPercent, double gPercent, double bPercent) => AddPercent(0, rPercent, gPercent, bPercent); - - /// - /// Adds the given RGB-percent values to this color. - /// - /// The alpha value to add. - /// The red value to add. - /// The green value to add. - /// The blue value to add. - /// The new color after the modification. - public Color AddPercent(double aPercent, double rPercent, double gPercent, double bPercent) - => new Color(APercent + aPercent, RPercent + rPercent, GPercent + gPercent, BPercent + bPercent); - - /// - /// Adds the given HSV values to this color. - /// - /// The hue value to add. - /// The saturation value to add. - /// The value value to add. - /// The new color after the modification. - public Color AddHSV(double hue, double saturation, double value) => AddHSV(0, hue, saturation, value); - - /// - /// Adds the given HSV values to this color. - /// - /// The alpha value to add. - /// The hue value to add. - /// The saturation value to add. - /// The value value to add. - /// The new color after the modification. - public Color AddHSV(int a, double hue, double saturation, double value) - => FromHSV(A + a, Hue + hue, Saturation + saturation, Value + value); - - /// - /// Adds the given alpha value to this color. - /// - /// The alpha value to add. - /// The new color after the modification. - public Color AddA(int a) => new Color(A + a, R, G, B); - - /// - /// Adds the given alpha-percent value to this color. - /// - /// The alpha value to add. - /// The new color after the modification. - public Color AddAPercent(double aPercent) => new Color(APercent + aPercent, RPercent, GPercent, BPercent); - - /// - /// Adds the given red value to this color. - /// - /// The red value to add. - /// The new color after the modification. - public Color AddR(int r) => new Color(A, R + r, G, B); - - /// - /// Adds the given red-percent value to this color. - /// - /// The red value to add. - /// The new color after the modification. - public Color AddRPercent(double rPercent) => new Color(APercent, RPercent + rPercent, GPercent, BPercent); - - /// - /// Adds the given green value to this color. - /// - /// The green value to add. - /// The new color after the modification. - public Color AddG(int g) => new Color(A, R, G + g, B); - - /// - /// Adds the given green-percent value to this color. - /// - /// The green value to add. - /// The new color after the modification. - public Color AddGPercent(double gPercent) => new Color(APercent, RPercent, GPercent + gPercent, BPercent); - - /// - /// Adds the given blue value to this color. - /// - /// The blue value to add. - /// The new color after the modification. - public Color AddB(int b) => new Color(A, R, G, B + b); - - /// - /// Adds the given blue-percent value to this color. - /// - /// The blue value to add. - /// The new color after the modification. - public Color AddBPercent(double bPercent) => new Color(APercent, RPercent, GPercent, BPercent + bPercent); - - /// - /// Adds the given hue value to this color. - /// - /// The hue value to add. - /// The new color after the modification. - public Color AddHue(double hue) => FromHSV(A, Hue + hue, Saturation, Value); - - /// - /// Adds the given saturation value to this color. - /// - /// The saturation value to add. - /// The new color after the modification. - public Color AddSaturation(double saturation) => FromHSV(A, Hue, Saturation + saturation, Value); - - /// - /// Adds the given value value to this color. - /// - /// The value value to add. - /// The new color after the modification. - public Color AddValue(double value) => FromHSV(A, Hue, Saturation, Value + value); - - #endregion - - #region Subtract - - /// - /// Subtracts the given RGB values to this color. - /// - /// The red value to subtract. - /// The green value to subtract. - /// The blue value to subtract. - /// The new color after the modification. - public Color SubtractRGB(int r, int g, int b) => SubtractRGB(0, r, g, b); - - /// - /// Subtracts the given RGB values to this color. - /// - /// The alpha value to subtract. - /// The red value to subtract. - /// The green value to subtract. - /// The blue value to subtract. - /// The new color after the modification. - public Color SubtractRGB(int a, int r, int g, int b) - => new Color(A - a, R - r, G - g, B - b); - - /// - /// Subtracts the given RGB-percent values to this color. - /// - /// The red value to subtract. - /// The green value to subtract. - /// The blue value to subtract. - /// The new color after the modification. - public Color SubtractPercent(double rPercent, double gPercent, double bPercent) => SubtractPercent(0, rPercent, gPercent, bPercent); - - /// - /// Subtracts the given RGB-percent values to this color. - /// - /// The alpha value to subtract. - /// The red value to subtract. - /// The green value to subtract. - /// The blue value to subtract. - /// The new color after the modification. - public Color SubtractPercent(double aPercent, double rPercent, double gPercent, double bPercent) - => new Color(APercent - aPercent, RPercent - rPercent, GPercent - gPercent, BPercent - bPercent); - - /// - /// Subtracts the given HSV values to this color. - /// - /// The hue value to subtract. - /// The saturation value to subtract. - /// The value value to subtract. - /// The new color after the modification. - public Color SubtractHSV(double hue, double saturation, double value) => SubtractHSV(0, hue, saturation, value); - - /// - /// Subtracts the given HSV values to this color. - /// - /// The alpha value to subtract. - /// The hue value to subtract. - /// The saturation value to subtract. - /// The value value to subtract. - /// The new color after the modification. - public Color SubtractHSV(int a, double hue, double saturation, double value) - => FromHSV(A - a, Hue - hue, Saturation - saturation, Value - value); - - /// - /// Subtracts the given alpha value to this color. - /// - /// The alpha value to subtract. - /// The new color after the modification. - public Color SubtractA(int a) => new Color(A - a, R, G, B); - - /// - /// Subtracts the given alpha-percent value to this color. - /// - /// The alpha value to subtract. - /// The new color after the modification. - public Color SubtractAPercent(double aPercent) => new Color(APercent - aPercent, RPercent, GPercent, BPercent); - - /// - /// Subtracts the given red value to this color. - /// - /// The red value to subtract. - /// The new color after the modification. - public Color SubtractR(int r) => new Color(A, R - r, G, B); - - /// - /// Subtracts the given red-percent value to this color. - /// - /// The red value to subtract. - /// The new color after the modification. - public Color SubtractRPercent(double rPercent) => new Color(APercent, RPercent - rPercent, GPercent, BPercent); - - /// - /// Subtracts the given green value to this color. - /// - /// The green value to subtract. - /// The new color after the modification. - public Color SubtractG(int g) => new Color(A, R, G - g, B); - - /// - /// Subtracts the given green-percent value to this color. - /// - /// The green value to subtract. - /// The new color after the modification. - public Color SubtractGPercent(double gPercent) => new Color(APercent, RPercent, GPercent - gPercent, BPercent); - - /// - /// Subtracts the given blue value to this color. - /// - /// The blue value to subtract. - /// The new color after the modification. - public Color SubtractB(int b) => new Color(A, R, G, B - b); - - /// - /// Subtracts the given blue-percent value to this color. - /// - /// The blue value to subtract. - /// The new color after the modification. - public Color SubtractBPercent(double bPercent) => new Color(APercent, RPercent, GPercent, BPercent - bPercent); - - /// - /// Subtracts the given hue value to this color. - /// - /// The hue value to subtract. - /// The new color after the modification. - public Color SubtractHue(double hue) => FromHSV(A, Hue - hue, Saturation, Value); - - /// - /// Subtracts the given saturation value to this color. - /// - /// The saturation value to subtract. - /// The new color after the modification. - public Color SubtractSaturation(double saturation) => FromHSV(A, Hue, Saturation - saturation, Value); - - /// - /// Subtracts the given value value to this color. - /// - /// The value value to subtract. - /// The new color after the modification. - public Color SubtractValue(double value) => FromHSV(A, Hue, Saturation, Value - value); - - #endregion - - #region Multiply - - /// - /// Multiplies the given RGB values to this color. - /// - /// The red value to multiply. - /// The green value to multiply. - /// The blue value to multiply. - /// The new color after the modification. - public Color MultiplyRGB(double r, double g, double b) => MultiplyRGB(1, r, g, b); - - /// - /// Multiplies the given RGB values to this color. - /// - /// The alpha value to multiply. - /// The red value to multiply. - /// The green value to multiply. - /// The blue value to multiply. - /// The new color after the modification. - public Color MultiplyRGB(double a, double r, double g, double b) - => new Color((int)Math.Round(A * a), (int)Math.Round(R * r), (int)Math.Round(G * g), (int)Math.Round(B * b)); - - /// - /// Multiplies the given RGB-percent values to this color. - /// - /// The red value to multiply. - /// The green value to multiply. - /// The blue value to multiply. - /// The new color after the modification. - public Color MultiplyPercent(double rPercent, double gPercent, double bPercent) => MultiplyPercent(1, rPercent, gPercent, bPercent); - - /// - /// Multiplies the given RGB-percent values to this color. - /// - /// The alpha value to multiply. - /// The red value to multiply. - /// The green value to multiply. - /// The blue value to multiply. - /// The new color after the modification. - public Color MultiplyPercent(double aPercent, double rPercent, double gPercent, double bPercent) - => new Color(APercent * aPercent, RPercent * rPercent, GPercent * gPercent, BPercent * bPercent); - - /// - /// Multiplies the given HSV values to this color. - /// - /// The hue value to multiply. - /// The saturation value to multiply. - /// The value value to multiply. - /// The new color after the modification. - public Color MultiplyHSV(double hue, double saturation, double value) => MultiplyHSV(1, hue, saturation, value); - - /// - /// Multiplies the given HSV values to this color. - /// - /// The alpha value to multiply. - /// The hue value to multiply. - /// The saturation value to multiply. - /// The value value to multiply. - /// The new color after the modification. - public Color MultiplyHSV(double a, double hue, double saturation, double value) - => FromHSV((int)Math.Round(A * a), Hue * hue, Saturation * saturation, Value * value); - - /// - /// Multiplies the given alpha value to this color. - /// - /// The alpha value to multiply. - /// The new color after the modification. - public Color MultiplyA(double a) => new Color((int)Math.Round(A * a), R, G, B); - - /// - /// Multiplies the given alpha-percent value to this color. - /// - /// The alpha value to multiply. - /// The new color after the modification. - public Color MultiplyAPercent(double aPercent) => new Color(APercent * aPercent, RPercent, GPercent, BPercent); - - /// - /// Multiplies the given red value to this color. - /// - /// The red value to multiply. - /// The new color after the modification. - public Color MultiplyR(double r) => new Color(A, (int)Math.Round(R * r), G, B); - - /// - /// Multiplies the given red-percent value to this color. - /// - /// The red value to multiply. - /// The new color after the modification. - public Color MultiplyRPercent(double rPercent) => new Color(APercent, RPercent * rPercent, GPercent, BPercent); - - /// - /// Multiplies the given green value to this color. - /// - /// The green value to multiply. - /// The new color after the modification. - public Color MultiplyG(double g) => new Color(A, R, (int)Math.Round(G * g), B); - - /// - /// Multiplies the given green-percent value to this color. - /// - /// The green value to multiply. - /// The new color after the modification. - public Color MultiplyGPercent(double gPercent) => new Color(APercent, RPercent, GPercent * gPercent, BPercent); - - /// - /// Multiplies the given blue value to this color. - /// - /// The blue value to multiply. - /// The new color after the modification. - public Color MultiplyB(double b) => new Color(A, R, G, (int)Math.Round(B * b)); - - /// - /// Multiplies the given blue-percent value to this color. - /// - /// The blue value to multiply. - /// The new color after the modification. - public Color MultiplyBPercent(double bPercent) => new Color(APercent, RPercent, GPercent, BPercent * bPercent); - - /// - /// Multiplies the given hue value to this color. - /// - /// The hue value to multiply. - /// The new color after the modification. - public Color MultiplyHue(double hue) => FromHSV(A, Hue * hue, Saturation, Value); - - /// - /// Multiplies the given saturation value to this color. - /// - /// The saturation value to multiply. - /// The new color after the modification. - public Color MultiplySaturation(double saturation) => FromHSV(A, Hue, Saturation * saturation, Value); - - /// - /// Multiplies the given value value to this color. - /// - /// The value value to multiply. - /// The new color after the modification. - public Color MultiplyValue(double value) => FromHSV(A, Hue, Saturation, Value * value); - - #endregion - - #region Divide - - /// - /// Divides the given RGB values to this color. - /// - /// The red value to divide. - /// The green value to divide. - /// The blue value to divide. - /// The new color after the modification. - public Color DivideRGB(double r, double g, double b) => DivideRGB(1, r, g, b); - - /// - /// Divides the given RGB values to this color. - /// - /// The alpha value to divide. - /// The red value to divide. - /// The green value to divide. - /// The blue value to divide. - /// The new color after the modification. - public Color DivideRGB(double a, double r, double g, double b) - => new Color((int)Math.Round(A / a), (int)Math.Round(R / r), (int)Math.Round(G / g), (int)Math.Round(B / b)); - - /// - /// Divides the given RGB-percent values to this color. - /// - /// The red value to divide. - /// The green value to divide. - /// The blue value to divide. - /// The new color after the modification. - public Color DividePercent(double rPercent, double gPercent, double bPercent) => DividePercent(1, rPercent, gPercent, bPercent); - - /// - /// Divides the given RGB-percent values to this color. - /// - /// The alpha value to divide. - /// The red value to divide. - /// The green value to divide. - /// The blue value to divide. - /// The new color after the modification. - public Color DividePercent(double aPercent, double rPercent, double gPercent, double bPercent) - => new Color(APercent / aPercent, RPercent / rPercent, GPercent / gPercent, BPercent / bPercent); - - /// - /// Divides the given HSV values to this color. - /// - /// The hue value to divide. - /// The saturation value to divide. - /// The value value to divide. - /// The new color after the modification. - public Color DivideHSV(double hue, double saturation, double value) => DivideHSV(1, hue, saturation, value); - - /// - /// Divides the given HSV values to this color. - /// - /// The alpha value to divide. - /// The hue value to divide. - /// The saturation value to divide. - /// The value value to divide. - /// The new color after the modification. - public Color DivideHSV(double a, double hue, double saturation, double value) - => FromHSV((int)Math.Round(A / a), Hue / hue, Saturation / saturation, Value / value); - - /// - /// Divides the given alpha value to this color. - /// - /// The alpha value to divide. - /// The new color after the modification. - public Color DivideA(double a) => new Color((int)Math.Round(A / a), R, G, B); - - /// - /// Divides the given alpha-percent value to this color. - /// - /// The alpha value to divide. - /// The new color after the modification. - public Color DivideAPercent(double aPercent) => new Color(APercent / aPercent, RPercent, GPercent, BPercent); - - /// - /// Divides the given red value to this color. - /// - /// The red value to divide. - /// The new color after the modification. - public Color DivideRValue(double r) => new Color(A, (int)Math.Round(R / r), G, B); - - /// - /// Divides the given red-percent value to this color. - /// - /// The red value to divide. - /// The new color after the modification. - public Color DivideRPercent(double rPercent) => new Color(APercent, RPercent / rPercent, GPercent, BPercent); - - /// - /// Divides the given green value to this color. - /// - /// The green value to divide. - /// The new color after the modification. - public Color DivideGValue(double g) => new Color(A, R, (int)Math.Round(G / g), B); - - /// - /// Divides the given green-percent value to this color. - /// - /// The green value to divide. - /// The new color after the modification. - public Color DivideGPercent(double gPercent) => new Color(APercent, RPercent, GPercent / gPercent, BPercent); - - /// - /// Divides the given blue value to this color. - /// - /// The blue value to divide. - /// The new color after the modification. - public Color DivideBValue(double b) => new Color(A, R, G, (int)Math.Round(B / b)); - - /// - /// Divides the given blue-percent value to this color. - /// - /// The blue value to divide. - /// The new color after the modification. - public Color DivideBPercent(double bPercent) => new Color(APercent, RPercent, GPercent, BPercent / bPercent); - - /// - /// Divides the given hue value to this color. - /// - /// The hue value to divide. - /// The new color after the modification. - public Color DivideHue(double hue) => FromHSV(A, Hue / hue, Saturation, Value); - - /// - /// Divides the given saturation value to this color. - /// - /// The saturation value to divide. - /// The new color after the modification. - public Color DivideSaturation(double saturation) => FromHSV(A, Hue, Saturation / saturation, Value); - - /// - /// Divides the given value value to this color. - /// - /// The value value to divide. - /// The new color after the modification. - public Color DivideValue(double value) => FromHSV(A, Hue, Saturation, Value / value); - - #endregion - - #region Set - - /// - /// Sets the given alpha value of this color. - /// - /// The alpha value to set. - /// The new color after the modification. - public Color SetA(int a) => new Color(a, R, G, B); - - /// - /// Sets the given alpha-percent value of this color. - /// - /// The alpha value to set. - /// The new color after the modification. - public Color SetAPercent(double aPercent) => new Color(aPercent, RPercent, GPercent, BPercent); - - /// - /// Sets the given red value of this color. - /// - /// The red value to set. - /// The new color after the modification. - public Color SetR(int r) => new Color(A, r, G, B); - - /// - /// Sets the given red-percent value of this color. - /// - /// The red value to set. - /// The new color after the modification. - public Color SetRPercent(double rPercent) => new Color(APercent, rPercent, GPercent, BPercent); - - /// - /// Sets the given green value of this color. - /// - /// The green value to set. - /// The new color after the modification. - public Color SetG(int g) => new Color(A, R, g, B); - - /// - /// Sets the given green-percent value of this color. - /// - /// The green value to set. - /// The new color after the modification. - public Color SetGPercent(double gPercent) => new Color(APercent, RPercent, gPercent, BPercent); - - /// - /// Sets the given blue value of this color. - /// - /// The blue value to set. - /// The new color after the modification. - public Color SetB(int b) => new Color(A, R, G, b); - - /// - /// Sets the given blue-percent value of this color. - /// - /// The blue value to set. - /// The new color after the modification. - public Color SetBPercent(double bPercent) => new Color(APercent, RPercent, GPercent, bPercent); - - /// - /// Sets the given hue value of this color. - /// - /// The hue value to set. - /// The new color after the modification. - public Color SetHue(double hue) => FromHSV(A, hue, Saturation, Value); - - /// - /// Sets the given saturation value of this color. - /// - /// The saturation value to set. - /// The new color after the modification. - public Color SetSaturation(double saturation) => FromHSV(A, Hue, saturation, Value); - - /// - /// Sets the given value value of this color. - /// - /// The value value to set. - /// The new color after the modification. - public Color SetValue(double value) => FromHSV(A, Hue, Saturation, value); - - #endregion - - #endregion - - #endregion - - #region Operators - - /// - /// Blends the provided colors as if would've been called on . - /// - /// The base color. - /// The color to blend. - /// The blended color. - public static Color operator +(Color color1, Color color2) => color1.Blend(color2); - - /// - /// Returns a value that indicates whether two specified are equal. - /// - /// The first to compare. - /// The second to compare. - /// true if and are equal; otherwise, false. - public static bool operator ==(Color color1, Color color2) => color1.Equals(color2); - - /// - /// Returns a value that indicates whether two specified are equal. - /// - /// The first to compare. - /// The second to compare. - /// true if and are not equal; otherwise, false. - public static bool operator !=(Color color1, Color color2) => !(color1 == color2); - - /// - /// Converts a of ARGB-components to a . - /// - /// The containing the components. - /// The color. - public static implicit operator Color((byte a, byte r, byte g, byte b) components) => new Color(components.a, components.r, components.g, components.b); - - /// - /// Converts a of HSV-components to a . - /// - /// The containing the components. - /// The color. - public static implicit operator Color((double hue, double saturation, double value) components) => new Color(components.hue, components.saturation, components.value); - - #endregion - } -} diff --git a/RGB.NET.Core/RGB.NET.Core.csproj.DotSettings b/RGB.NET.Core/RGB.NET.Core.csproj.DotSettings index d88c562..d216495 100644 --- a/RGB.NET.Core/RGB.NET.Core.csproj.DotSettings +++ b/RGB.NET.Core/RGB.NET.Core.csproj.DotSettings @@ -1,6 +1,8 @@  True + True True + True True True True diff --git a/RGB.NET.Decorators/Brush/FlashDecorator.cs b/RGB.NET.Decorators/Brush/FlashDecorator.cs index dd84f2c..618b243 100644 --- a/RGB.NET.Decorators/Brush/FlashDecorator.cs +++ b/RGB.NET.Decorators/Brush/FlashDecorator.cs @@ -77,7 +77,7 @@ namespace RGB.NET.Decorators.Brush #region Methods /// - public Color ManipulateColor(Rectangle rectangle, BrushRenderTarget renderTarget, Color color) => color.SetAPercent(_currentValue); + public Color ManipulateColor(Rectangle rectangle, BrushRenderTarget renderTarget, Color color) => color.SetA(_currentValue); /// protected override void Update(double deltaTime) diff --git a/RGB.NET.Devices.Asus/Generic/AsusUpdateQueue.cs b/RGB.NET.Devices.Asus/Generic/AsusUpdateQueue.cs index e6d4a6a..28fa936 100644 --- a/RGB.NET.Devices.Asus/Generic/AsusUpdateQueue.cs +++ b/RGB.NET.Devices.Asus/Generic/AsusUpdateQueue.cs @@ -57,9 +57,9 @@ namespace RGB.NET.Devices.Asus foreach (KeyValuePair data in dataSet) { int index = ((int)data.Key) * 3; - ColorData[index] = data.Value.R; - ColorData[index + 1] = data.Value.B; - ColorData[index + 2] = data.Value.G; + ColorData[index] = data.Value.GetR(); + ColorData[index + 1] = data.Value.GetB(); + ColorData[index + 2] = data.Value.GetG(); } _updateAction(_handle, ColorData); diff --git a/RGB.NET.Devices.CoolerMaster/Generic/CoolerMasterUpdateQueue.cs b/RGB.NET.Devices.CoolerMaster/Generic/CoolerMasterUpdateQueue.cs index 045b6e4..ecc4cd6 100644 --- a/RGB.NET.Devices.CoolerMaster/Generic/CoolerMasterUpdateQueue.cs +++ b/RGB.NET.Devices.CoolerMaster/Generic/CoolerMasterUpdateQueue.cs @@ -39,7 +39,7 @@ namespace RGB.NET.Devices.CoolerMaster foreach (KeyValuePair data in dataSet) { (int row, int column) = ((int, int))data.Key; - _CoolerMasterSDK.SetLedColor(row, column, data.Value.R, data.Value.G, data.Value.B, _deviceIndex); + _CoolerMasterSDK.SetLedColor(row, column, data.Value.GetR(), data.Value.GetG(), data.Value.GetB(), _deviceIndex); } _CoolerMasterSDK.RefreshLed(false, _deviceIndex); diff --git a/RGB.NET.Devices.Corsair/CorsairDeviceProvider.cs b/RGB.NET.Devices.Corsair/CorsairDeviceProvider.cs index 8a9f81c..d048397 100644 --- a/RGB.NET.Devices.Corsair/CorsairDeviceProvider.cs +++ b/RGB.NET.Devices.Corsair/CorsairDeviceProvider.cs @@ -215,7 +215,8 @@ namespace RGB.NET.Devices.Corsair for (int channel = 0; channel < channelsInfo.channelsCount; channel++) { - CorsairLedId referenceLed = channel == 0 ? CorsairLedId.CustomDeviceChannel1Led1 : CorsairLedId.CustomDeviceChannel2Led1; + CorsairLedId referenceLed = GetChannelReferenceId(info.CorsairDeviceType, channel); + if (referenceLed == CorsairLedId.Invalid) continue; _CorsairChannelInfo channelInfo = (_CorsairChannelInfo)Marshal.PtrToStructure(channelInfoPtr, typeof(_CorsairChannelInfo)); @@ -247,6 +248,23 @@ namespace RGB.NET.Devices.Corsair } } + private static CorsairLedId GetChannelReferenceId(CorsairDeviceType deviceType, int channel) + { + if (deviceType == CorsairDeviceType.Cooler) + return CorsairLedId.CustomLiquidCoolerChannel1Led1; + else + { + switch (channel) + { + case 0: return CorsairLedId.CustomDeviceChannel1Led1; + case 1: return CorsairLedId.CustomDeviceChannel2Led1; + case 2: return CorsairLedId.CustomDeviceChannel3Led1; + } + } + + return CorsairLedId.Invalid; + } + private void AddSpecialParts(ICorsairRGBDevice device) { if (device.DeviceInfo.Model.Equals("K95 RGB Platinum", StringComparison.OrdinalIgnoreCase)) diff --git a/RGB.NET.Devices.Corsair/Generic/CorsairDeviceUpdateQueue.cs b/RGB.NET.Devices.Corsair/Generic/CorsairDeviceUpdateQueue.cs index 9540018..664a6e6 100644 --- a/RGB.NET.Devices.Corsair/Generic/CorsairDeviceUpdateQueue.cs +++ b/RGB.NET.Devices.Corsair/Generic/CorsairDeviceUpdateQueue.cs @@ -46,9 +46,9 @@ namespace RGB.NET.Devices.Corsair _CorsairLedColor color = new _CorsairLedColor { ledId = (int)data.Key, - r = data.Value.R, - g = data.Value.G, - b = data.Value.B + r = data.Value.GetR(), + g = data.Value.GetG(), + b = data.Value.GetB() }; Marshal.StructureToPtr(color, addPtr, false); diff --git a/RGB.NET.Devices.Logitech/HID/DeviceChecker.cs b/RGB.NET.Devices.Logitech/HID/DeviceChecker.cs index 1a15104..b56b03c 100644 --- a/RGB.NET.Devices.Logitech/HID/DeviceChecker.cs +++ b/RGB.NET.Devices.Logitech/HID/DeviceChecker.cs @@ -45,6 +45,7 @@ namespace RGB.NET.Devices.Logitech.HID { ("G903", RGBDeviceType.Mouse, 0xC086, 2, "default", @"Mice\G903"), ("G900", RGBDeviceType.Mouse, 0xC539, 2, "default", @"Mice\G900"), + ("G703", RGBDeviceType.Mouse, 0xC087, 2, "default", @"Mice\G703"), ("G403", RGBDeviceType.Mouse, 0xC083, 2, "default", @"Mice\G403"), ("G303", RGBDeviceType.Mouse, 0xC080, 2, "default", @"Mice\G303"), ("G Pro", RGBDeviceType.Mouse, 0xC085, 1, "default", @"Mice\GPro"), diff --git a/RGB.NET.Devices.Logitech/PerDevice/LogitechPerDeviceUpdateQueue.cs b/RGB.NET.Devices.Logitech/PerDevice/LogitechPerDeviceUpdateQueue.cs index 23e1b69..8ea4c6a 100644 --- a/RGB.NET.Devices.Logitech/PerDevice/LogitechPerDeviceUpdateQueue.cs +++ b/RGB.NET.Devices.Logitech/PerDevice/LogitechPerDeviceUpdateQueue.cs @@ -32,9 +32,9 @@ namespace RGB.NET.Devices.Logitech Color color = dataSet.Values.First(); _LogitechGSDK.LogiLedSetTargetDevice(LogitechDeviceCaps.DeviceRGB); - _LogitechGSDK.LogiLedSetLighting((int)Math.Round(color.RPercent * 100), - (int)Math.Round(color.GPercent * 100), - (int)Math.Round(color.BPercent * 100)); + _LogitechGSDK.LogiLedSetLighting((int)Math.Round(color.R * 100), + (int)Math.Round(color.G * 100), + (int)Math.Round(color.B * 100)); } #endregion diff --git a/RGB.NET.Devices.Logitech/PerKey/BitmapMapping.cs b/RGB.NET.Devices.Logitech/PerKey/BitmapMapping.cs index cfb5cd9..9bd9a6e 100644 --- a/RGB.NET.Devices.Logitech/PerKey/BitmapMapping.cs +++ b/RGB.NET.Devices.Logitech/PerKey/BitmapMapping.cs @@ -158,10 +158,10 @@ namespace RGB.NET.Devices.Logitech [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void SetColor(byte[] bitmap, int offset, Color color) { - bitmap[offset] = color.B; - bitmap[offset + 1] = color.G; - bitmap[offset + 2] = color.R; - bitmap[offset + 3] = color.A; + bitmap[offset] = color.GetB(); + bitmap[offset + 1] = color.GetG(); + bitmap[offset + 2] = color.GetR(); + bitmap[offset + 3] = color.GetA(); } #endregion diff --git a/RGB.NET.Devices.Logitech/PerKey/LogitechPerKeyUpdateQueue.cs b/RGB.NET.Devices.Logitech/PerKey/LogitechPerKeyUpdateQueue.cs index 02f7dd0..a166a3a 100644 --- a/RGB.NET.Devices.Logitech/PerKey/LogitechPerKeyUpdateQueue.cs +++ b/RGB.NET.Devices.Logitech/PerKey/LogitechPerKeyUpdateQueue.cs @@ -51,9 +51,9 @@ namespace RGB.NET.Devices.Logitech } else _LogitechGSDK.LogiLedSetLightingForKeyWithKeyName((int)customData, - (int)Math.Round(data.Value.RPercent * 100), - (int)Math.Round(data.Value.GPercent * 100), - (int)Math.Round(data.Value.BPercent * 100)); + (int)Math.Round(data.Value.R * 100), + (int)Math.Round(data.Value.G * 100), + (int)Math.Round(data.Value.B * 100)); } if (usesBitmap) diff --git a/RGB.NET.Devices.Logitech/Zone/LogitechZoneUpdateQueue.cs b/RGB.NET.Devices.Logitech/Zone/LogitechZoneUpdateQueue.cs index ab0062c..a95ca98 100644 --- a/RGB.NET.Devices.Logitech/Zone/LogitechZoneUpdateQueue.cs +++ b/RGB.NET.Devices.Logitech/Zone/LogitechZoneUpdateQueue.cs @@ -56,9 +56,9 @@ namespace RGB.NET.Devices.Logitech { int zone = (int)data.Key; _LogitechGSDK.LogiLedSetLightingForTargetZone(_deviceType, zone, - (int)Math.Round(data.Value.RPercent * 100), - (int)Math.Round(data.Value.GPercent * 100), - (int)Math.Round(data.Value.BPercent * 100)); + (int)Math.Round(data.Value.R * 100), + (int)Math.Round(data.Value.G * 100), + (int)Math.Round(data.Value.B * 100)); } } diff --git a/RGB.NET.Devices.Msi/Generic/MsiRGBDevice.cs b/RGB.NET.Devices.Msi/Generic/MsiRGBDevice.cs index 2387690..5fe18ab 100644 --- a/RGB.NET.Devices.Msi/Generic/MsiRGBDevice.cs +++ b/RGB.NET.Devices.Msi/Generic/MsiRGBDevice.cs @@ -66,7 +66,7 @@ namespace RGB.NET.Devices.Msi { string deviceType = DeviceInfo.MsiDeviceType; foreach (Led led in leds) - _MsiSDK.SetLedColor(deviceType, (int)led.CustomData, led.Color.R, led.Color.G, led.Color.B); + _MsiSDK.SetLedColor(deviceType, (int)led.CustomData, led.Color.GetR(), led.Color.GetG(), led.Color.GetB()); } } diff --git a/RGB.NET.Devices.Novation/Generic/LimitedColorUpdateQueue.cs b/RGB.NET.Devices.Novation/Generic/LimitedColorUpdateQueue.cs index 65460e2..a3570c9 100644 --- a/RGB.NET.Devices.Novation/Generic/LimitedColorUpdateQueue.cs +++ b/RGB.NET.Devices.Novation/Generic/LimitedColorUpdateQueue.cs @@ -40,14 +40,16 @@ namespace RGB.NET.Devices.Novation /// The novation-representation of the . protected virtual int ConvertColor(Color color) { - if ((color.Hue >= 330) || (color.Hue < 30)) - return (int)Math.Ceiling(color.Value * 3); // red with brightness 1, 2 or 3 + (double hue, double saturation, double value) = color.GetHSV(); - if ((color.Hue >= 30) && (color.Hue < 90)) // yellow with brightness 17, 34 or 51 - return (int)Math.Ceiling(color.Value * 3) * 17; + if ((hue >= 330) || (hue < 30)) + return (int)Math.Ceiling(value * 3); // red with brightness 1, 2 or 3 - if ((color.Hue >= 90) && (color.Hue < 150)) // green with brightness 16, 32 or 48 - return (int)Math.Ceiling(color.Value * 3) * 16; + if ((hue >= 30) && (hue < 90)) // yellow with brightness 17, 34 or 51 + return (int)Math.Ceiling(value * 3) * 17; + + if ((hue >= 90) && (hue < 150)) // green with brightness 16, 32 or 48 + return (int)Math.Ceiling(value * 3) * 16; return 0; } diff --git a/RGB.NET.Devices.Razer/Native/_Color.cs b/RGB.NET.Devices.Razer/Native/_Color.cs index 66b07ea..a36cc78 100644 --- a/RGB.NET.Devices.Razer/Native/_Color.cs +++ b/RGB.NET.Devices.Razer/Native/_Color.cs @@ -21,7 +21,7 @@ namespace RGB.NET.Devices.Razer.Native #region Constructors public _Color(Color color) - : this(color.R, color.G, color.B) { } + : this(color.GetR(), color.GetG(), color.GetB()) { } public _Color(byte red, byte green, byte blue) : this() diff --git a/RGB.NET.Devices.SoIP/Client/SoIPClientRGBDevice.cs b/RGB.NET.Devices.SoIP/Client/SoIPClientRGBDevice.cs index 0ba9697..c3bec0f 100644 --- a/RGB.NET.Devices.SoIP/Client/SoIPClientRGBDevice.cs +++ b/RGB.NET.Devices.SoIP/Client/SoIPClientRGBDevice.cs @@ -66,7 +66,7 @@ namespace RGB.NET.Devices.SoIP.Client List<(LedId, Color)> leds = message.MessageString.Split(';').Select(x => { string[] led = x.Split('|'); - return ((LedId)Enum.Parse(typeof(LedId), led[0]), Color.FromHexString(led[1])); + return ((LedId)Enum.Parse(typeof(LedId), led[0]), RGBColor.FromHexString(led[1])); }).ToList(); lock (_syncbackCache) foreach ((LedId ledId, Color color) in leds) diff --git a/RGB.NET.Devices.SoIP/Server/SoIPServerRGBDevice.cs b/RGB.NET.Devices.SoIP/Server/SoIPServerRGBDevice.cs index 79c20a1..5a5285e 100644 --- a/RGB.NET.Devices.SoIP/Server/SoIPServerRGBDevice.cs +++ b/RGB.NET.Devices.SoIP/Server/SoIPServerRGBDevice.cs @@ -61,7 +61,7 @@ namespace RGB.NET.Devices.SoIP.Server tcpClient.GetStream().WriteAsync(messageData, 0, messageData.Length); } - private string GetLedString(IEnumerable leds) => string.Join(";", leds.Select(x => x.Id.ToString() + "|" + x.Color.AsARGBHexString())); + private string GetLedString(IEnumerable leds) => string.Join(";", leds.Select(x => x.Id.ToString() + "|" + x.Color.AsARGBHexString(false))); /// public override void Dispose() diff --git a/RGB.NET.Devices.SoIP/Server/SoIPServerUpdateQueue.cs b/RGB.NET.Devices.SoIP/Server/SoIPServerUpdateQueue.cs index 999ef9c..1d100ce 100644 --- a/RGB.NET.Devices.SoIP/Server/SoIPServerUpdateQueue.cs +++ b/RGB.NET.Devices.SoIP/Server/SoIPServerUpdateQueue.cs @@ -46,7 +46,7 @@ namespace RGB.NET.Devices.SoIP.Server } } - private string GetLedString(Dictionary dataSet) => string.Join(";", dataSet.Select(x => x.Key.ToString() + "|" + x.Value.AsARGBHexString())); + private string GetLedString(Dictionary dataSet) => string.Join(";", dataSet.Select(x => x.Key.ToString() + "|" + x.Value.AsARGBHexString(false))); #endregion } diff --git a/RGB.NET.Devices.SteelSeries/API/Model/CoreProps.cs b/RGB.NET.Devices.SteelSeries/API/Model/CoreProps.cs new file mode 100644 index 0000000..a3f1f85 --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/API/Model/CoreProps.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace RGB.NET.Devices.SteelSeries.API.Model +{ + internal class CoreProps + { + [JsonProperty(PropertyName = "address")] + public string Address { get; set; } + } +} diff --git a/RGB.NET.Devices.SteelSeries/API/Model/Event.cs b/RGB.NET.Devices.SteelSeries/API/Model/Event.cs new file mode 100644 index 0000000..ba3eb60 --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/API/Model/Event.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace RGB.NET.Devices.SteelSeries.API.Model +{ + internal class Event + { + #region Properties & Fields + + [JsonProperty("game")] + public string Game { get; set; } + + [JsonProperty("event")] + public string Name { get; set; } + + [JsonProperty("data")] + public Dictionary Data { get; } = new Dictionary(); + + #endregion + + #region Constructors + + public Event() + { } + + public Event(Game game, string name) + { + this.Name = name; + Game = game.Name; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.SteelSeries/API/Model/Game.cs b/RGB.NET.Devices.SteelSeries/API/Model/Game.cs new file mode 100644 index 0000000..d93c7e4 --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/API/Model/Game.cs @@ -0,0 +1,30 @@ +using Newtonsoft.Json; + +namespace RGB.NET.Devices.SteelSeries.API.Model +{ + internal class Game + { + #region Properties & Fields + + [JsonProperty("game")] + public string Name { get; set; } + + [JsonProperty("game_display_name")] + public string DisplayName { get; set; } + + #endregion + + #region Constructors + + public Game() + { } + + public Game(string name, string displayName) + { + Name = name; + DisplayName = displayName; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.SteelSeries/API/Model/GoLispHandler.cs b/RGB.NET.Devices.SteelSeries/API/Model/GoLispHandler.cs new file mode 100644 index 0000000..27d5315 --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/API/Model/GoLispHandler.cs @@ -0,0 +1,30 @@ +using Newtonsoft.Json; + +namespace RGB.NET.Devices.SteelSeries.API.Model +{ + internal class GoLispHandler + { + #region Properties & Fields + + [JsonProperty("game")] + public string Game { get; set; } + + [JsonProperty("golisp")] + public string Handler { get; set; } + + #endregion + + #region Constructors + + public GoLispHandler() + { } + + public GoLispHandler(Game game, string handler) + { + this.Handler = handler; + Game = game.Name; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.SteelSeries/API/SteelSeriesSDK.cs b/RGB.NET.Devices.SteelSeries/API/SteelSeriesSDK.cs new file mode 100644 index 0000000..be7f5ac --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/API/SteelSeriesSDK.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net.Http; +using System.Runtime.InteropServices; +using System.Text; +using Newtonsoft.Json; +using RGB.NET.Devices.SteelSeries.API.Model; + +namespace RGB.NET.Devices.SteelSeries.API +{ + internal static class SteelSeriesSDK + { + #region Constants + + private const string GAME_NAME = "RGBNET"; + private const string GAME_DISPLAYNAME = "RGB.NET"; + private const string EVENT_NAME = "UPDATELEDS"; + private const string HANDLER = @"(handler """ + EVENT_NAME + @""" + (lambda (data) + (let* ((device (value: data)) + (zoneData (frame: data)) + (zones (frame-keys zoneData))) + (do ((zoneDo zones (cdr zoneDo))) + ((nil? zoneDo)) + (let* ((zone (car zoneDo)) + (color (get-slot zoneData zone))) + (on-device device show-on-zone: color zone))))))"; + + private const string CORE_PROPS_WINDOWS = "%PROGRAMDATA%/SteelSeries/SteelSeries Engine 3/coreProps.json"; + private const string CORE_PROPS_OSX = "/Library/Application Support/SteelSeries Engine 3/coreProps.json"; + + #endregion + + #region Properties & Fields + // ReSharper disable InconsistentNaming + + private static readonly HttpClient _client = new HttpClient(); + private static readonly Game _game = new Game(GAME_NAME, GAME_DISPLAYNAME); + private static readonly Event _event = new Event(_game, EVENT_NAME); + private static string _baseUrl; + + internal static bool IsInitialized => !string.IsNullOrWhiteSpace(_baseUrl); + + // ReSharper restore InconsistentNaming + #endregion + + #region Methods + + internal static bool Initialize() + { + try + { + string corePropsPath = GetCorePropsPath(); + if (!string.IsNullOrWhiteSpace(corePropsPath) && File.Exists(corePropsPath)) + { + CoreProps coreProps = JsonConvert.DeserializeObject(File.ReadAllText(corePropsPath)); + _baseUrl = coreProps.Address; + if (!_baseUrl.StartsWith("http://", StringComparison.Ordinal)) + _baseUrl = "http://" + _baseUrl; + + RegisterGame(_game); + RegisterGoLispHandler(new GoLispHandler(_game, HANDLER)); + } + } + catch + { + _baseUrl = null; + } + return IsInitialized; + } + + internal static void UpdateLeds(string device, Dictionary data) + { + _event.Data.Clear(); + _event.Data.Add("value", device); + _event.Data.Add("frame", data); + + TriggerEvent(_event); + } + + internal static void SendHeartbeat() => SendHeartbeat(_game); + + internal static void ResetLeds() => StopGame(_game); + + internal static void Dispose() + { + ResetLeds(); + _client.Dispose(); + } + + private static string TriggerEvent(Event e) => PostJson("/game_event", e); + private static string RegisterGoLispHandler(GoLispHandler handler) => PostJson("/load_golisp_handlers", handler); + private static string RegisterEvent(Event e) => PostJson("/register_game_event", e); + private static string UnregisterEvent(Event e) => PostJson("/remove_game_event", e); + private static string RegisterGame(Game game) => PostJson("/game_metadata", game); + private static string UnregisterGame(Game game) => PostJson("/remove_game", game); + private static string StopGame(Game game) => PostJson("/stop_game", game); + private static string SendHeartbeat(Game game) => PostJson("/game_heartbeat", game); + + private static string PostJson(string urlSuffix, object o) + { + string payload = JsonConvert.SerializeObject(o); + return _client.PostAsync(_baseUrl + urlSuffix, new StringContent(payload, Encoding.UTF8, "application/json")).Result.Content.ReadAsStringAsync().Result; + } + + private static string GetCorePropsPath() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + return Environment.ExpandEnvironmentVariables(CORE_PROPS_WINDOWS); + + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + return CORE_PROPS_OSX; + + throw new InvalidOperationException("Unknown operating system."); + } + + #endregion + } +} diff --git a/RGB.NET.Devices.SteelSeries/Attribute/APIName.cs b/RGB.NET.Devices.SteelSeries/Attribute/APIName.cs new file mode 100644 index 0000000..5091145 --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/Attribute/APIName.cs @@ -0,0 +1,20 @@ +namespace RGB.NET.Devices.SteelSeries +{ + internal class APIName : System.Attribute + { + #region Properties & Fields + + public string Name { get; set; } + + #endregion + + #region Constructors + + public APIName(string name) + { + this.Name = name; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.SteelSeries/Attribute/SteelSeriesEnumExtension.cs b/RGB.NET.Devices.SteelSeries/Attribute/SteelSeriesEnumExtension.cs new file mode 100644 index 0000000..3331a50 --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/Attribute/SteelSeriesEnumExtension.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace RGB.NET.Devices.SteelSeries +{ + internal static class SteelSeriesEnumExtension + { + #region Properties & Fields + // ReSharper disable InconsistentNaming + + private static readonly Dictionary _deviceTypeNames = new Dictionary(); + private static readonly Dictionary _ledIdNames = new Dictionary(); + + // ReSharper restore InconsistentNaming + #endregion + + #region Methods + + internal static string GetAPIName(this SteelSeriesDeviceType deviceType) + { + if (!_deviceTypeNames.TryGetValue(deviceType, out string apiName)) + _deviceTypeNames.Add(deviceType, apiName = GetAPIName(typeof(SteelSeriesDeviceType), deviceType)); + + return apiName; + } + + internal static string GetAPIName(this SteelSeriesLedId ledId) + { + if (!_ledIdNames.TryGetValue(ledId, out string apiName)) + _ledIdNames.Add(ledId, apiName = GetAPIName(typeof(SteelSeriesLedId), ledId)); + + return apiName; + } + + private static string GetAPIName(Type type, Enum value) + { + MemberInfo[] memInfo = type.GetMember(value.ToString()); + if (memInfo.Length == 0) return null; + return (memInfo.FirstOrDefault()?.GetCustomAttributes(typeof(APIName), false).FirstOrDefault() as APIName)?.Name; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.SteelSeries/Enum/SteelSeriesDeviceType.cs b/RGB.NET.Devices.SteelSeries/Enum/SteelSeriesDeviceType.cs new file mode 100644 index 0000000..2a426af --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/Enum/SteelSeriesDeviceType.cs @@ -0,0 +1,23 @@ +namespace RGB.NET.Devices.SteelSeries +{ + public enum SteelSeriesDeviceType + { + [APIName("rgb-per-key-zones")] + PerKey, + + [APIName("rgb-1-zone")] + OneZone, + + [APIName("rgb-2-zone")] + TwoZone, + + [APIName("rgb-3-zone")] + ThreeZone, + + [APIName("rgb-4-zone")] + FourZone, + + [APIName("rgb-5-zone")] + FiveZone + } +} diff --git a/RGB.NET.Devices.SteelSeries/Enum/SteelSeriesLedId.cs b/RGB.NET.Devices.SteelSeries/Enum/SteelSeriesLedId.cs new file mode 100644 index 0000000..87244e0 --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/Enum/SteelSeriesLedId.cs @@ -0,0 +1,243 @@ +namespace RGB.NET.Devices.SteelSeries +{ + public enum SteelSeriesLedId + { + [APIName("one")] + ZoneOne, + [APIName("two")] + ZoneTwo, + [APIName("three")] + ZoneThree, + [APIName("four")] + ZoneFour, + [APIName("five")] + ZoneFive, + + [APIName("logo")] + Logo, + [APIName("a")] + A, + [APIName("b")] + B, + [APIName("c")] + C, + [APIName("d")] + D, + [APIName("e")] + E, + [APIName("f")] + F, + [APIName("g")] + G, + [APIName("h")] + H, + [APIName("i")] + I, + [APIName("j")] + J, + [APIName("k")] + K, + [APIName("l")] + L, + [APIName("m")] + M, + [APIName("n")] + N, + [APIName("o")] + O, + [APIName("p")] + P, + [APIName("q")] + Q, + [APIName("r")] + R, + [APIName("s")] + S, + [APIName("t")] + T, + [APIName("u")] + U, + [APIName("v")] + V, + [APIName("w")] + W, + [APIName("x")] + X, + [APIName("y")] + Y, + [APIName("z")] + Z, + [APIName("keyboard-1")] + Keyboard1, + [APIName("keyboard-2")] + Keyboard2, + [APIName("keyboard-3")] + Keyboard3, + [APIName("keyboard-4")] + Keyboard4, + [APIName("keyboard-5")] + Keyboard5, + [APIName("keyboard-6")] + Keyboard6, + [APIName("keyboard-7")] + Keyboard7, + [APIName("keyboard-8")] + Keyboard8, + [APIName("keyboard-9")] + Keyboard9, + [APIName("keyboard-0")] + Keyboard0, + [APIName("return")] + Return, + [APIName("escape")] + Escape, + [APIName("backspace")] + Backspace, + [APIName("tab")] + Tab, + [APIName("spacebar")] + Spacebar, + [APIName("caps")] + Caps, + [APIName("dash")] + Dash, + [APIName("equal")] + Equal, + [APIName("l-bracket")] + LBracket, + [APIName("r-bracket")] + RBracket, + [APIName("backslash")] + Backslash, + [APIName("pound")] + Pound, + [APIName("semicolon")] + Semicolon, + [APIName("quote")] + Quote, + [APIName("backquote")] + Backqoute, + [APIName("comma")] + Comma, + [APIName("period")] + Period, + [APIName("slash")] + Slash, + [APIName("f1")] + F1, + [APIName("f2")] + F2, + [APIName("f3")] + F3, + [APIName("f4")] + F4, + [APIName("f5")] + F5, + [APIName("f6")] + F6, + [APIName("f7")] + F7, + [APIName("f8")] + F8, + [APIName("f9")] + F9, + [APIName("f10")] + F10, + [APIName("f11")] + F11, + [APIName("f12")] + F12, + [APIName("printscreen")] + PrintScreen, + [APIName("scrolllock")] + ScrollLock, + [APIName("pause")] + Pause, + [APIName("insert")] + Insert, + [APIName("home")] + Home, + [APIName("pageup")] + PageUp, + [APIName("delete")] + Delete, + [APIName("end")] + End, + [APIName("pagedown")] + PageDown, + [APIName("rightarrow")] + RightArrow, + [APIName("leftarrow")] + LeftArrow, + [APIName("downarrow")] + DownArrow, + [APIName("uparrow")] + UpArrow, + [APIName("keypad-num-lock")] + KeypadNumLock, + [APIName("keypad-divide")] + KeypadDivide, + [APIName("keypad-times")] + KeypadTimes, + [APIName("keypad-minus")] + KeypadMinus, + [APIName("keypad-plus")] + KeypadPlus, + [APIName("keypad-enter")] + KeypadEnter, + [APIName("keypad-period")] + KeypadPeriod, + [APIName("keypad-1")] + Keypad1, + [APIName("keypad-2")] + Keypad2, + [APIName("keypad-3")] + Keypad3, + [APIName("keypad-4")] + Keypad4, + [APIName("keypad-5")] + Keypad5, + [APIName("keypad-6")] + Keypad6, + [APIName("keypad-7")] + Keypad7, + [APIName("keypad-8")] + Keypad8, + [APIName("keypad-9")] + Keypad9, + [APIName("keypad-0")] + Keypad0, + [APIName("l-ctrl")] + LCtrl, + [APIName("l-shift")] + LShift, + [APIName("l-alt")] + LAlt, + [APIName("l-win")] + LWin, + [APIName("r-ctrl")] + RCtrl, + [APIName("r-shift")] + RShift, + [APIName("r-alt")] + RAlt, + [APIName("r-win")] + RWin, + [APIName("ss-key")] + SSKey, + [APIName("win-menu")] + WinMenu, + [APIName("m0")] + M0, + [APIName("m1")] + M1, + [APIName("m2")] + M2, + [APIName("m3")] + M3, + [APIName("m4")] + M4, + [APIName("m5")] + M5, + } +} diff --git a/RGB.NET.Devices.SteelSeries/Generic/ISteelSeriesRGBDevice.cs b/RGB.NET.Devices.SteelSeries/Generic/ISteelSeriesRGBDevice.cs new file mode 100644 index 0000000..57364d9 --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/Generic/ISteelSeriesRGBDevice.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using RGB.NET.Core; + +namespace RGB.NET.Devices.SteelSeries +{ + /// + /// Represents a steelseries RGB-device. + /// + internal interface ISteelSeriesRGBDevice : IRGBDevice + { + void Initialize(UpdateQueue updateQueue, Dictionary ledMapping); + } +} diff --git a/RGB.NET.Devices.SteelSeries/Generic/SteelSeriesDeviceUpdateQueue.cs b/RGB.NET.Devices.SteelSeries/Generic/SteelSeriesDeviceUpdateQueue.cs new file mode 100644 index 0000000..9b43d5c --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/Generic/SteelSeriesDeviceUpdateQueue.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; +using System.Linq; +using RGB.NET.Core; +using RGB.NET.Devices.SteelSeries.API; +using RGB.NET.Devices.SteelSeries.Helper; + +namespace RGB.NET.Devices.SteelSeries +{ + /// + /// + /// Represents the update-queue performing updates for steelseries devices. + /// + internal class SteelSeriesDeviceUpdateQueue : UpdateQueue + { + #region Properties & Fields + + private string _deviceType; + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The update trigger used by this queue. + /// The device type used to identify the device. + public SteelSeriesDeviceUpdateQueue(IDeviceUpdateTrigger updateTrigger, string deviceType) + : base(updateTrigger) + { + this._deviceType = deviceType; + } + + #endregion + + #region Methods + + /// + protected override void Update(Dictionary dataSet) + { + SteelSeriesSDK.UpdateLeds(_deviceType, dataSet.ToDictionary(x => ((SteelSeriesLedId)x.Key).GetAPIName(), x => x.Value.ToIntArray())); + } + + #endregion + } +} diff --git a/RGB.NET.Devices.SteelSeries/Generic/SteelSeriesRGBDevice.cs b/RGB.NET.Devices.SteelSeries/Generic/SteelSeriesRGBDevice.cs new file mode 100644 index 0000000..b30d85b --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/Generic/SteelSeriesRGBDevice.cs @@ -0,0 +1,88 @@ +using System.Collections.Generic; +using System.Linq; +using RGB.NET.Core; + +namespace RGB.NET.Devices.SteelSeries +{ + /// + /// + /// + /// Represents a SteelSeries-device. (keyboard, mouse, headset, mousepad). + /// + public class SteelSeriesRGBDevice : AbstractRGBDevice, ISteelSeriesRGBDevice + { + #region Properties & Fields + + private Dictionary _ledMapping; + + /// + /// + /// Gets information about the . + /// + public override SteelSeriesRGBDeviceInfo DeviceInfo { get; } + + /// + /// Gets or sets the update queue performing updates for this device. + /// + // ReSharper disable once MemberCanBePrivate.Global + protected UpdateQueue UpdateQueue { get; set; } + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The generic information provided by SteelSeries for the device. + internal SteelSeriesRGBDevice(SteelSeriesRGBDeviceInfo info) + { + this.DeviceInfo = info; + } + + #endregion + + #region Methods + + /// + /// Initializes the device. + /// + void ISteelSeriesRGBDevice.Initialize(UpdateQueue updateQueue, Dictionary ledMapping) + { + _ledMapping = ledMapping; + + int counter = 0; + foreach (KeyValuePair mapping in ledMapping) + InitializeLed(mapping.Key, new Rectangle((counter++) * 10, 0, 10, 10)); + + InitializeLayout(); + + if (Size == Size.Invalid) + { + Rectangle ledRectangle = new Rectangle(this.Select(x => x.LedRectangle)); + Size = ledRectangle.Size + new Size(ledRectangle.Location.X, ledRectangle.Location.Y); + } + + UpdateQueue = updateQueue; + } + + protected override object CreateLedCustomData(LedId ledId) => _ledMapping[ledId]; + + /// + protected override void UpdateLeds(IEnumerable ledsToUpdate) => UpdateQueue.SetData(ledsToUpdate.Where(x => x.Color.A > 0)); + + /// + /// Initializes the and of the device. + /// + protected virtual void InitializeLayout() + { + if (!(DeviceInfo is SteelSeriesRGBDeviceInfo info)) return; + + string layout = info.ImageLayout; + string layoutPath = info.LayoutPath; + ApplyLayoutFromFile(PathHelper.GetAbsolutePath($@"Layouts\SteelSeries\{layoutPath}.xml"), layout, true); + } + + #endregion + } +} diff --git a/RGB.NET.Devices.SteelSeries/Generic/SteelSeriesRGBDeviceInfo.cs b/RGB.NET.Devices.SteelSeries/Generic/SteelSeriesRGBDeviceInfo.cs new file mode 100644 index 0000000..afb2e7d --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/Generic/SteelSeriesRGBDeviceInfo.cs @@ -0,0 +1,72 @@ +using System; +using RGB.NET.Core; + +namespace RGB.NET.Devices.SteelSeries +{ + /// + /// + /// Represents a generic information for a SteelSeries-. + /// + public class SteelSeriesRGBDeviceInfo : IRGBDeviceInfo + { + #region Properties & Fields + + /// + public RGBDeviceType DeviceType { get; } + + /// + public string DeviceName { get; } + + /// + public string Manufacturer => "SteelSeries"; + + /// + public string Model { get; } + + /// + public Uri Image { get; set; } + + /// + public RGBDeviceLighting Lighting => RGBDeviceLighting.Key; + + /// + public bool SupportsSyncBack => false; + + public SteelSeriesDeviceType SteelSeriesDeviceType { get; } + + /// + /// Gets the layout used to decide which images to load. + /// + internal string ImageLayout { get; } + + /// + /// Gets the path/name of the layout-file. + /// + internal string LayoutPath { get; } + + #endregion + + #region Constructors + + /// + /// Internal constructor of managed . + /// + /// The type of the . + /// The represented device model. + /// The lighting-capabilities of the device. + /// The layout used to decide which images to load. + /// The path/name of the layout-file. + internal SteelSeriesRGBDeviceInfo(RGBDeviceType deviceType, string model, SteelSeriesDeviceType steelSeriesDeviceType, string imageLayout, string layoutPath) + { + this.DeviceType = deviceType; + this.Model = model; + this.SteelSeriesDeviceType = steelSeriesDeviceType; + this.ImageLayout = imageLayout; + this.LayoutPath = layoutPath; + + DeviceName = $"{Manufacturer} {Model}"; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.SteelSeries/HID/DeviceChecker.cs b/RGB.NET.Devices.SteelSeries/HID/DeviceChecker.cs new file mode 100644 index 0000000..249cf2a --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/HID/DeviceChecker.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using System.Linq; +using HidSharp; +using RGB.NET.Core; +using DeviceDataList = System.Collections.Generic.List<(string model, RGB.NET.Core.RGBDeviceType deviceType, int id, RGB.NET.Devices.SteelSeries.SteelSeriesDeviceType steelSeriesDeviceType, string imageLayout, string layoutPath, System.Collections.Generic.Dictionary ledMapping)>; +using LedMapping = System.Collections.Generic.Dictionary; + +namespace RGB.NET.Devices.SteelSeries.HID +{ + internal static class DeviceChecker + { + #region Constants + + private const int VENDOR_ID = 0x1038; + + //TODO DarthAffe 16.02.2019: Add devices + private static readonly DeviceDataList DEVICES = new DeviceDataList + { + ("Rival 500", RGBDeviceType.Mouse, 0x170E, SteelSeriesDeviceType.TwoZone, "default", @"Mice\Rival500", new LedMapping { { LedId.Mouse1, SteelSeriesLedId.ZoneOne}, + { LedId.Mouse2, SteelSeriesLedId.ZoneTwo} }), + }; + + #endregion + + #region Properties & Fields + + public static DeviceDataList ConnectedDevices { get; } = new DeviceDataList(); + + #endregion + + #region Methods + + internal static void LoadDeviceList(RGBDeviceType loadFilter) + { + ConnectedDevices.Clear(); + + HashSet ids = new HashSet(DeviceList.Local.GetHidDevices(VENDOR_ID).Select(x => x.ProductID).Distinct()); + DeviceDataList connectedDevices = DEVICES.Where(d => ids.Contains(d.id) && loadFilter.HasFlag(d.deviceType)).ToList(); + + List connectedDeviceTypes = connectedDevices.Select(d => d.steelSeriesDeviceType).ToList(); + foreach (SteelSeriesDeviceType deviceType in connectedDeviceTypes) + ConnectedDevices.Add(connectedDevices.Where(d => d.steelSeriesDeviceType == deviceType).OrderByDescending(d => d.ledMapping.Count).First()); + } + + #endregion + } +} diff --git a/RGB.NET.Devices.SteelSeries/Helper/ColorExtensions.cs b/RGB.NET.Devices.SteelSeries/Helper/ColorExtensions.cs new file mode 100644 index 0000000..793dc02 --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/Helper/ColorExtensions.cs @@ -0,0 +1,9 @@ +using RGB.NET.Core; + +namespace RGB.NET.Devices.SteelSeries.Helper +{ + internal static class ColorExtensions + { + internal static int[] ToIntArray(this Color color) => new int[] { color.GetR(), color.GetB(), color.GetG() }; + } +} diff --git a/RGB.NET.Devices.SteelSeries/RGB.NET.Devices.SteelSeries.csproj b/RGB.NET.Devices.SteelSeries/RGB.NET.Devices.SteelSeries.csproj new file mode 100644 index 0000000..bfe4e06 --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/RGB.NET.Devices.SteelSeries.csproj @@ -0,0 +1,62 @@ + + + netstandard2.0 + win7-x86;win7-x64 + + Darth Affe + Wyrez + en-US + en-US + RGB.NET.Devices.SteelSeries + RGB.NET.Devices.SteelSeries + RGB.NET.Devices.SteelSeries + RGB.NET.Devices.SteelSeries + RGB.NET.Devices.SteelSeries + SteelSeries-Device-Implementations of RGB.NET + SteelSeries-Device-Implementations of RGB.NET, a C# (.NET) library for accessing various RGB-peripherals + Copyright © Wyrez 2017 + Copyright © Wyrez 2017 + http://lib.arge.be/icon.png + https://github.com/DarthAffe/RGB.NET + https://raw.githubusercontent.com/DarthAffe/RGB.NET/master/LICENSE + Github + https://github.com/DarthAffe/RGB.NET + True + + + + 0.0.1 + 0.0.1 + 0.0.1 + + ..\bin\ + true + True + True + latest + + + + NETCORE;NETSTANDARD;NETSTANDARD2_0 + + + + $(DefineConstants);TRACE;DEBUG + true + full + false + + + + pdbonly + true + $(NoWarn);CS1591;CS1572;CS1573 + $(DefineConstants);RELEASE + + + + + + + + \ No newline at end of file diff --git a/RGB.NET.Devices.SteelSeries/RGB.NET.Devices.SteelSeries.csproj.DotSettings b/RGB.NET.Devices.SteelSeries/RGB.NET.Devices.SteelSeries.csproj.DotSettings new file mode 100644 index 0000000..20d4792 --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/RGB.NET.Devices.SteelSeries.csproj.DotSettings @@ -0,0 +1,4 @@ + + True + True + True \ No newline at end of file diff --git a/RGB.NET.Devices.SteelSeries/SteelSeriesDeviceProvider.cs b/RGB.NET.Devices.SteelSeries/SteelSeriesDeviceProvider.cs new file mode 100644 index 0000000..432c51b --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/SteelSeriesDeviceProvider.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using RGB.NET.Core; +using RGB.NET.Devices.SteelSeries.API; +using RGB.NET.Devices.SteelSeries.HID; + +namespace RGB.NET.Devices.SteelSeries +{ + /// + /// + /// Represents a device provider responsible for SteelSeries- devices. + /// + public class SteelSeriesDeviceProvider : IRGBDeviceProvider + { + #region Properties & Fields + + private static SteelSeriesDeviceProvider _instance; + /// + /// Gets the singleton instance. + /// + public static SteelSeriesDeviceProvider Instance => _instance ?? new SteelSeriesDeviceProvider(); + + /// + /// + /// Indicates if the SDK is initialized and ready to use. + /// + public bool IsInitialized { get; private set; } + + /// + /// + /// Gets whether the application has exclusive access to the SDK or not. + /// + public bool HasExclusiveAccess => false; + + /// + public IEnumerable Devices { get; private set; } + + /// + /// The used to trigger the updates for SteelSeries devices. + /// + public DeviceUpdateTrigger UpdateTrigger { get; } + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// Thrown if this constructor is called even if there is already an instance of this class. + public SteelSeriesDeviceProvider() + { + if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(SteelSeriesDeviceProvider)}"); + _instance = this; + + UpdateTrigger = new DeviceUpdateTrigger(); + } + + #endregion + + #region Methods + + /// + public bool Initialize(RGBDeviceType loadFilter = RGBDeviceType.All, bool exclusiveAccessIfPossible = false, bool throwExceptions = false) + { + try + { + IsInitialized = false; + + UpdateTrigger?.Stop(); + + if (!SteelSeriesSDK.IsInitialized) + SteelSeriesSDK.Initialize(); + + IList devices = new List(); + DeviceChecker.LoadDeviceList(loadFilter); + + try + { + foreach ((string model, RGBDeviceType deviceType, int _, SteelSeriesDeviceType steelSeriesDeviceType, string imageLayout, string layoutPath, Dictionary ledMapping) in DeviceChecker.ConnectedDevices) + { + ISteelSeriesRGBDevice device = new SteelSeriesRGBDevice(new SteelSeriesRGBDeviceInfo(deviceType, model, steelSeriesDeviceType, imageLayout, layoutPath)); + SteelSeriesDeviceUpdateQueue updateQueue = new SteelSeriesDeviceUpdateQueue(UpdateTrigger, steelSeriesDeviceType.GetAPIName()); + device.Initialize(updateQueue, ledMapping); + devices.Add(device); + } + } + catch { if (throwExceptions) throw; } + + UpdateTrigger?.Start(); + + Devices = new ReadOnlyCollection(devices); + IsInitialized = true; + } + catch + { + IsInitialized = false; + if (throwExceptions) throw; + return false; + } + + return true; + } + + /// + public void ResetDevices() + { + if (IsInitialized) + try + { + SteelSeriesSDK.ResetLeds(); + } + catch {/* shit happens */} + } + + /// + public void Dispose() + { + try + { + SteelSeriesSDK.Dispose(); + } + catch {/* shit happens */} + } + + #endregion + } +} diff --git a/RGB.NET.Devices.SteelSeries/SteelSeriesDeviceProviderLoader.cs b/RGB.NET.Devices.SteelSeries/SteelSeriesDeviceProviderLoader.cs new file mode 100644 index 0000000..ffe1938 --- /dev/null +++ b/RGB.NET.Devices.SteelSeries/SteelSeriesDeviceProviderLoader.cs @@ -0,0 +1,25 @@ +using RGB.NET.Core; + +namespace RGB.NET.Devices.SteelSeries +{ + /// + /// Represents a device provider loaded used to dynamically load steelseries devices into an application. + /// + // ReSharper disable once UnusedMember.Global + public class SteelSeriesDeviceProviderLoader : IRGBDeviceProviderLoader + { + #region Properties & Fields + + /// + public bool RequiresInitialization => false; + + #endregion + + #region Methods + + /// + public IRGBDeviceProvider GetDeviceProvider() => SteelSeriesDeviceProvider.Instance; + + #endregion + } +} diff --git a/RGB.NET.Devices.WS281X/Arduino/ArduinoWS2812USBUpdateQueue.cs b/RGB.NET.Devices.WS281X/Arduino/ArduinoWS2812USBUpdateQueue.cs index 696f54e..e9fea46 100644 --- a/RGB.NET.Devices.WS281X/Arduino/ArduinoWS2812USBUpdateQueue.cs +++ b/RGB.NET.Devices.WS281X/Arduino/ArduinoWS2812USBUpdateQueue.cs @@ -62,7 +62,7 @@ namespace RGB.NET.Devices.WS281X.Arduino dataBuffer[0] = (byte)((channel << 4) | UPDATE_COMMAND[0]); int i = 1; foreach ((byte _, byte r, byte g, byte b) in channelData.OrderBy(x => x.Item1.key) - .Select(x => x.Value)) + .Select(x => x.Value.GetRGBBytes())) { dataBuffer[i++] = r; dataBuffer[i++] = g; diff --git a/RGB.NET.Devices.WS281X/Bitwizard/BitwizardWS2812USBUpdateQueue.cs b/RGB.NET.Devices.WS281X/Bitwizard/BitwizardWS2812USBUpdateQueue.cs index 4ea1152..5b70008 100644 --- a/RGB.NET.Devices.WS281X/Bitwizard/BitwizardWS2812USBUpdateQueue.cs +++ b/RGB.NET.Devices.WS281X/Bitwizard/BitwizardWS2812USBUpdateQueue.cs @@ -39,7 +39,7 @@ namespace RGB.NET.Devices.WS281X.Bitwizard protected override IEnumerable GetCommands(Dictionary dataSet) { foreach (KeyValuePair data in dataSet) - yield return $"pix {(int)data.Key} {data.Value.AsRGBHexString()}"; + yield return $"pix {(int)data.Key} {data.Value.AsRGBHexString(false)}"; } #endregion diff --git a/RGB.NET.sln b/RGB.NET.sln index b6ea9d4..c61b57f 100644 --- a/RGB.NET.sln +++ b/RGB.NET.sln @@ -39,6 +39,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RGB.NET.Groups", "RGB.NET.G EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RGB.NET.Devices.WS281X", "RGB.NET.Devices.WS281X\RGB.NET.Devices.WS281X.csproj", "{0AD382DA-E999-4F74-9658-8D402EE9BF3F}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RGB.NET.Devices.SteelSeries", "RGB.NET.Devices.SteelSeries\RGB.NET.Devices.SteelSeries.csproj", "{FFDE4387-60F2-47B6-9704-3A57D02B8C64}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{92D7C263-D4C9-4D26-93E2-93C1F9C2CD16}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RGB.NET.Core.Tests", "Tests\RGB.NET.Core.Tests\RGB.NET.Core.Tests.csproj", "{A3FD5AD7-040A-47CA-A278-53493A25FF8A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -109,6 +115,14 @@ Global {0AD382DA-E999-4F74-9658-8D402EE9BF3F}.Debug|Any CPU.Build.0 = Debug|Any CPU {0AD382DA-E999-4F74-9658-8D402EE9BF3F}.Release|Any CPU.ActiveCfg = Release|Any CPU {0AD382DA-E999-4F74-9658-8D402EE9BF3F}.Release|Any CPU.Build.0 = Release|Any CPU + {FFDE4387-60F2-47B6-9704-3A57D02B8C64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FFDE4387-60F2-47B6-9704-3A57D02B8C64}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FFDE4387-60F2-47B6-9704-3A57D02B8C64}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FFDE4387-60F2-47B6-9704-3A57D02B8C64}.Release|Any CPU.Build.0 = Release|Any CPU + {A3FD5AD7-040A-47CA-A278-53493A25FF8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A3FD5AD7-040A-47CA-A278-53493A25FF8A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A3FD5AD7-040A-47CA-A278-53493A25FF8A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A3FD5AD7-040A-47CA-A278-53493A25FF8A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -129,6 +143,8 @@ Global {8725C448-818C-41F7-B23F-F97E062BF233} = {EBC33090-8494-4DF4-B4B6-64D0E531E93F} {6FEBDC9E-909D-4EE2-B003-EDFBEF5FFF40} = {EBC33090-8494-4DF4-B4B6-64D0E531E93F} {0AD382DA-E999-4F74-9658-8D402EE9BF3F} = {D13032C6-432E-4F43-8A32-071133C22B16} + {FFDE4387-60F2-47B6-9704-3A57D02B8C64} = {D13032C6-432E-4F43-8A32-071133C22B16} + {A3FD5AD7-040A-47CA-A278-53493A25FF8A} = {92D7C263-D4C9-4D26-93E2-93C1F9C2CD16} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {7F222AD4-1F9E-4AAB-9D69-D62372D4C1BA} diff --git a/Tests/RGB.NET.Core.Tests/Color/ColorTest.cs b/Tests/RGB.NET.Core.Tests/Color/ColorTest.cs new file mode 100644 index 0000000..20b17a1 --- /dev/null +++ b/Tests/RGB.NET.Core.Tests/Color/ColorTest.cs @@ -0,0 +1,395 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace RGB.NET.Core.Tests.Color +{ + [TestClass] + public class ColorTest + { + #region Basics + + [TestMethod] + public void VerifyTransparent() + { + Core.Color transparent = Core.Color.Transparent; + + Assert.AreEqual(0, transparent.GetA(), "A is not 0"); + Assert.AreEqual(0, transparent.GetR(), "R is not 0"); + Assert.AreEqual(0, transparent.GetG(), "G is not 0"); + Assert.AreEqual(0, transparent.GetB(), "B is not 0"); + } + + [TestMethod] + public void ToStringTest() + { + Core.Color color = new Core.Color(255, 120, 13, 1); + + Assert.AreEqual("[A: 255, R: 120, G: 13, B: 1]", color.ToString()); + } + + #region HashCode + + [TestMethod] + public void GetHashCodeTestEqual() + { + Core.Color color1 = new Core.Color(100, 68, 32, 255); + Core.Color color2 = new Core.Color(100, 68, 32, 255); + + Assert.AreEqual(color1.GetHashCode(), color2.GetHashCode()); + } + + [TestMethod] + public void GetHashCodeTestNotEqualA() + { + Core.Color color1 = new Core.Color(100, 68, 32, 255); + Core.Color color2 = new Core.Color(99, 68, 32, 255); + + Assert.AreNotEqual(color1.GetHashCode(), color2.GetHashCode()); + } + + [TestMethod] + public void GetHashCodeTestNotEqualR() + { + Core.Color color1 = new Core.Color(100, 68, 32, 255); + Core.Color color2 = new Core.Color(100, 69, 32, 255); + + Assert.AreNotEqual(color1.GetHashCode(), color2.GetHashCode()); + } + + [TestMethod] + public void GetHashCodeTestNotEqualG() + { + Core.Color color1 = new Core.Color(100, 68, 32, 255); + Core.Color color2 = new Core.Color(100, 68, 200, 255); + + Assert.AreNotEqual(color1.GetHashCode(), color2.GetHashCode()); + } + + [TestMethod] + public void GetHashCodeTestNotEqualB() + { + Core.Color color1 = new Core.Color(100, 68, 32, 255); + Core.Color color2 = new Core.Color(100, 68, 32, 0); + + Assert.AreNotEqual(color1.GetHashCode(), color2.GetHashCode()); + } + + #endregion + + #region Equality + + [TestMethod] + public void EqualityTestEqual() + { + Core.Color color1 = new Core.Color(100, 68, 32, 255); + Core.Color color2 = new Core.Color(100, 68, 32, 255); + + Assert.IsTrue(color1.Equals(color2), $"Equals returns false on equal colors {color1} and {color2}"); + Assert.IsTrue(color1 == color2, $"Equal-operator returns false on equal colors {color1} and {color2}"); + Assert.IsFalse(color1 != color2, $"Not-Equal-operator returns true on equal colors {color1} and {color2}"); + } + + [TestMethod] + public void EqualityTestNotEqualA() + { + Core.Color color1 = new Core.Color(100, 68, 32, 255); + Core.Color color2 = new Core.Color(99, 68, 32, 255); + + Assert.IsFalse(color1.Equals(color2), $"Equals returns true on not equal colors {color1} and {color2}"); + Assert.IsFalse(color1 == color2, $"Equal-operator returns true on not equal colors {color1} and {color2}"); + Assert.IsTrue(color1 != color2, $"Not-Equal-operator returns false on not equal colors {color1} and {color2}"); + } + + [TestMethod] + public void EqualityTestNotEqualR() + { + Core.Color color1 = new Core.Color(100, 68, 32, 255); + Core.Color color2 = new Core.Color(100, 69, 32, 255); + + Assert.IsFalse(color1.Equals(color2), $"Equals returns true on not equal colors {color1} and {color2}"); + Assert.IsFalse(color1 == color2, $"Equal-operator returns true on not equal colors {color1} and {color2}"); + Assert.IsTrue(color1 != color2, $"Not-Equal-operator returns false on not equal colors {color1} and {color2}"); + } + + [TestMethod] + public void EqualityTestNotEqualG() + { + Core.Color color1 = new Core.Color(100, 68, 32, 255); + Core.Color color2 = new Core.Color(100, 68, 200, 255); + + Assert.IsFalse(color1.Equals(color2), $"Equals returns true on not equal colors {color1} and {color2}"); + Assert.IsFalse(color1 == color2, $"Equal-operator returns true on not equal colors {color1} and {color2}"); + Assert.IsTrue(color1 != color2, $"Not-Equal-operator returns false on not equal colors {color1} and {color2}"); + } + + [TestMethod] + public void EqualityTestNotEqualB() + { + Core.Color color1 = new Core.Color(100, 68, 32, 255); + Core.Color color2 = new Core.Color(100, 68, 32, 0); + + Assert.IsFalse(color1.Equals(color2), $"Equals returns true on not equal colors {color1} and {color2}"); + Assert.IsFalse(color1 == color2, $"Equal-operator returns true on not equal colors {color1} and {color2}"); + Assert.IsTrue(color1 != color2, $"Not-Equal-operator returns false on not equal colors {color1} and {color2}"); + } + + #endregion + + #endregion + + #region Constructors + + [TestMethod] + public void RGBByteConstructorTest() + { + Core.Color color = new Core.Color((byte)10, (byte)120, (byte)255); + + Assert.AreEqual(255, color.GetA(), "A is not 255"); + Assert.AreEqual(10, color.GetR(), "R is not 10"); + Assert.AreEqual(120, color.GetG(), "G is not 120"); + Assert.AreEqual(255, color.GetB(), "B is not 255"); + } + + [TestMethod] + public void ARGBByteConstructorTest() + { + Core.Color color = new Core.Color((byte)200, (byte)10, (byte)120, (byte)255); + + Assert.AreEqual(200, color.GetA(), "A is not 200"); + Assert.AreEqual(10, color.GetR(), "R is not 10"); + Assert.AreEqual(120, color.GetG(), "G is not 120"); + Assert.AreEqual(255, color.GetB(), "B is not 255"); + } + + [TestMethod] + public void RGBIntConstructorTest() + { + Core.Color color = new Core.Color(10, 120, 255); + + Assert.AreEqual(255, color.GetA(), "A is not 255"); + Assert.AreEqual(10, color.GetR(), "R is not 10"); + Assert.AreEqual(120, color.GetG(), "G is not 120"); + Assert.AreEqual(255, color.GetB(), "B is not 255"); + } + + [TestMethod] + public void ARGBIntConstructorTest() + { + Core.Color color = new Core.Color(200, 10, 120, 255); + + Assert.AreEqual(200, color.GetA(), "A is not 200"); + Assert.AreEqual(10, color.GetR(), "R is not 10"); + Assert.AreEqual(120, color.GetG(), "G is not 120"); + Assert.AreEqual(255, color.GetB(), "B is not 255"); + } + + [TestMethod] + public void RGBIntConstructorClampTest() + { + Core.Color color1 = new Core.Color(256, 256, 256); + + Assert.AreEqual(255, color1.GetA(), "A is not 255"); + Assert.AreEqual(255, color1.GetR(), "R is not 255"); + Assert.AreEqual(255, color1.GetG(), "G is not 255"); + Assert.AreEqual(255, color1.GetB(), "B is not 255"); + + Core.Color color2 = new Core.Color(-1, -1, -1); + + Assert.AreEqual(255, color2.GetA(), "A is not 255"); + Assert.AreEqual(0, color2.GetR(), "R is not 0"); + Assert.AreEqual(0, color2.GetG(), "G is not 0"); + Assert.AreEqual(0, color2.GetB(), "B is not 0"); + } + + [TestMethod] + public void ARGBIntConstructorClampTest() + { + Core.Color color = new Core.Color(256, 256, 256, 256); + + Assert.AreEqual(255, color.GetA(), "A is not 255"); + Assert.AreEqual(255, color.GetR(), "R is not 255"); + Assert.AreEqual(255, color.GetG(), "G is not 255"); + Assert.AreEqual(255, color.GetB(), "B is not 255"); + + Core.Color color2 = new Core.Color(-1, -1, -1, -1); + + Assert.AreEqual(0, color2.GetA(), "A is not 0"); + Assert.AreEqual(0, color2.GetR(), "R is not 0"); + Assert.AreEqual(0, color2.GetG(), "G is not 0"); + Assert.AreEqual(0, color2.GetB(), "B is not 0"); + } + + [TestMethod] + public void RGBPercentConstructorTest() + { + Core.Color color = new Core.Color(0.25341, 0.55367, 1); + + Assert.AreEqual(1, color.A, DoubleExtensions.TOLERANCE, "A is not 1"); + Assert.AreEqual(0.25341, color.R, DoubleExtensions.TOLERANCE, "R is not 0.25341"); + Assert.AreEqual(0.55367, color.G, DoubleExtensions.TOLERANCE, "G is not 0.55367"); + Assert.AreEqual(1, color.B, DoubleExtensions.TOLERANCE, "B is not 1"); + } + + [TestMethod] + public void ARGBPercentConstructorTest() + { + Core.Color color = new Core.Color(0.3315, 0.25341, 0.55367, 1); + + Assert.AreEqual(0.3315, color.A, DoubleExtensions.TOLERANCE, "A is not 0.3315"); + Assert.AreEqual(0.25341, color.R, DoubleExtensions.TOLERANCE, "R is not 0.25341"); + Assert.AreEqual(0.55367, color.G, DoubleExtensions.TOLERANCE, "G is not 0.55367"); + Assert.AreEqual(1, color.B, DoubleExtensions.TOLERANCE, "B is not 1"); + } + + [TestMethod] + public void RGBPercentConstructorClampTest() + { + Core.Color color1 = new Core.Color(1.1, 1.1, 1.1); + + Assert.AreEqual(1, color1.A, "A is not 1"); + Assert.AreEqual(1, color1.R, "R is not 1"); + Assert.AreEqual(1, color1.G, "G is not 1"); + Assert.AreEqual(1, color1.B, "B is not 1"); + + Core.Color color2 = new Core.Color(-1.0, -1.0, -1.0); + + Assert.AreEqual(1, color2.A, "A is not 1"); + Assert.AreEqual(0, color2.R, "R is not 0"); + Assert.AreEqual(0, color2.G, "G is not 0"); + Assert.AreEqual(0, color2.B, "B is not 0"); + } + + [TestMethod] + public void ARGBPercentConstructorClampTest() + { + Core.Color color1 = new Core.Color(1.1, 1.1, 1.1, 1.1); + + Assert.AreEqual(1, color1.A, "A is not 1"); + Assert.AreEqual(1, color1.R, "R is not 1"); + Assert.AreEqual(1, color1.G, "G is not 1"); + Assert.AreEqual(1, color1.B, "B is not 1"); + + Core.Color color2 = new Core.Color(-1.0, -1.0, -1.0, -1.0); + + Assert.AreEqual(0, color2.A, "A is not 0"); + Assert.AreEqual(0, color2.R, "R is not 0"); + Assert.AreEqual(0, color2.G, "G is not 0"); + Assert.AreEqual(0, color2.B, "B is not 0"); + } + + [TestMethod] + public void CloneConstructorTest() + { + Core.Color referennceColor = new Core.Color(200, 10, 120, 255); + Core.Color color = new Core.Color(referennceColor); + + Assert.AreEqual(200, color.GetA(), "A is not 200"); + Assert.AreEqual(10, color.GetR(), "R is not 10"); + Assert.AreEqual(120, color.GetG(), "G is not 120"); + Assert.AreEqual(255, color.GetB(), "B is not 255"); + } + + #endregion + + #region Conversion + + [TestMethod] + public void ColorFromComponentsTest() + { + Core.Color color = (255, 120, 13, 1); + + Assert.AreEqual(255, color.GetA(), $"A doesn't equal the used component. ({color.GetA()} != 255)"); + Assert.AreEqual(120, color.GetR(), $"R doesn't equal the used component. ({color.GetR()} != 120)"); + Assert.AreEqual(13, color.GetG(), $"G doesn't equal the used component. ({color.GetG()} != 13)"); + Assert.AreEqual(1, color.GetB(), $"B doesn't equal the used component. ({color.GetB()} != 1)"); + } + + [TestMethod] + public void DesconstructTest() + { + (byte a, byte r, byte g, byte b) = new Core.Color(255, 120, 13, 1).GetRGBBytes(); + + Assert.AreEqual(255, a, $"A doesn't equal the color. ({a} != 255)"); + Assert.AreEqual(120, r, $"R doesn't equal the color. ({r} != 120)"); + Assert.AreEqual(13, g, $"G doesn't equal the color. ({g} != 13)"); + Assert.AreEqual(1, b, $"B doesn't equal the color. ({b} != 1)"); + } + + [TestMethod] + public void AToPercentTest() + { + Core.Color color1 = new Core.Color(0, 0, 0, 0); + Assert.AreEqual(0, color1.A); + + Core.Color color2 = new Core.Color(255, 0, 0, 0); + Assert.AreEqual(1, color2.A); + + Core.Color color3 = new Core.Color(128, 0, 0, 0); + Assert.AreEqual(128 / 255.0, color3.A); + + Core.Color color4 = new Core.Color(30, 0, 0, 0); + Assert.AreEqual(30 / 255.0, color4.A); + + Core.Color color5 = new Core.Color(201, 0, 0, 0); + Assert.AreEqual(201 / 255.0, color5.A); + } + + [TestMethod] + public void RToPercentTest() + { + Core.Color color1 = new Core.Color(0, 0, 0, 0); + Assert.AreEqual(0, color1.R); + + Core.Color color2 = new Core.Color(0, 255, 0, 0); + Assert.AreEqual(1, color2.R); + + Core.Color color3 = new Core.Color(0, 128, 0, 0); + Assert.AreEqual(128 / 255.0, color3.R); + + Core.Color color4 = new Core.Color(0, 30, 0, 0); + Assert.AreEqual(30 / 255.0, color4.R); + + Core.Color color5 = new Core.Color(0, 201, 0, 0); + Assert.AreEqual(201 / 255.0, color5.R); + } + + [TestMethod] + public void GToPercentTest() + { + Core.Color color1 = new Core.Color(0, 0, 0, 0); + Assert.AreEqual(0, color1.G); + + Core.Color color2 = new Core.Color(0, 0, 255, 0); + Assert.AreEqual(1, color2.G); + + Core.Color color3 = new Core.Color(0, 0, 128, 0); + Assert.AreEqual(128 / 255.0, color3.G); + + Core.Color color4 = new Core.Color(0, 0, 30, 0); + Assert.AreEqual(30 / 255.0, color4.G); + + Core.Color color5 = new Core.Color(0, 0, 201, 0); + Assert.AreEqual(201 / 255.0, color5.G); + } + + [TestMethod] + public void BToPercentTest() + { + Core.Color color1 = new Core.Color(0, 0, 0, 0); + Assert.AreEqual(0, color1.B); + + Core.Color color2 = new Core.Color(0, 0, 0, 255); + Assert.AreEqual(1, color2.B); + + Core.Color color3 = new Core.Color(0, 0, 0, 128); + Assert.AreEqual(128 / 255.0, color3.B); + + Core.Color color4 = new Core.Color(0, 0, 0, 30); + Assert.AreEqual(30 / 255.0, color4.B); + + Core.Color color5 = new Core.Color(0, 0, 0, 201); + Assert.AreEqual(201 / 255.0, color5.B); + } + + #endregion + } +} diff --git a/Tests/RGB.NET.Core.Tests/Color/HSVColorTest.cs b/Tests/RGB.NET.Core.Tests/Color/HSVColorTest.cs new file mode 100644 index 0000000..823a203 --- /dev/null +++ b/Tests/RGB.NET.Core.Tests/Color/HSVColorTest.cs @@ -0,0 +1,284 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace RGB.NET.Core.Tests.Color +{ + [TestClass] + public class HSVColorTest + { + #region Manipulation + + #region Add + + [TestMethod] + public void AddHueTest() + { + Core.Color baseColor = HSVColor.Create(180, 0.5, 0.5); + Core.Color result = baseColor.AddHSV(hue: 30); + + Assert.AreEqual(HSVColor.Create(210, 0.5, 0.5), result); + } + + [TestMethod] + public void AddHueWrapTest() + { + Core.Color baseColor = HSVColor.Create(180, 0.5, 0.5); + Core.Color result = baseColor.AddHSV(hue: 220); + + Assert.AreEqual(HSVColor.Create(40, 0.5, 0.5), result); + } + + [TestMethod] + public void AddSaturationTest() + { + Core.Color baseColor = HSVColor.Create(180, 0.5, 0.5); + Core.Color result = baseColor.AddHSV(saturation: 0.3); + + Assert.AreEqual(HSVColor.Create(180, 0.8, 0.5), result); + } + + [TestMethod] + public void AddSaturationClampTest() + { + Core.Color baseColor = HSVColor.Create(180, 0.5, 0.5); + Core.Color result = baseColor.AddHSV(saturation: 0.8); + + Assert.AreEqual(HSVColor.Create(180, 1.0, 0.5), result); + } + + [TestMethod] + public void AddValueTest() + { + Core.Color baseColor = HSVColor.Create(180, 0.5, 0.5); + Core.Color result = baseColor.AddHSV(value: 0.3); + + Assert.AreEqual(HSVColor.Create(180, 0.5, 0.8), result); + } + + [TestMethod] + public void AddValueClampTest() + { + Core.Color baseColor = HSVColor.Create(180, 0.5, 0.5); + Core.Color result = baseColor.AddHSV(value: 0.8); + + Assert.AreEqual(HSVColor.Create(180, 0.5, 1.0), result); + } + + #endregion + + #region Subtract + + [TestMethod] + public void SubtractHueTest() + { + Core.Color baseColor = HSVColor.Create(180, 0.5, 0.5); + Core.Color result = baseColor.SubtractHSV(hue: 30); + + Assert.AreEqual(HSVColor.Create(150, 0.5, 0.5), result); + } + + [TestMethod] + public void SubtractHueWrapTest() + { + Core.Color baseColor = HSVColor.Create(180, 0.5, 0.5); + Core.Color result = baseColor.SubtractHSV(hue: 220); + + Assert.AreEqual(HSVColor.Create(320, 0.5, 0.5), result); + } + + [TestMethod] + public void SubtractSaturationTest() + { + Core.Color baseColor = HSVColor.Create(180, 0.5, 0.5); + Core.Color result = baseColor.SubtractHSV(saturation: 0.3); + + Assert.AreEqual(HSVColor.Create(180, 0.2, 0.5), result); + } + + [TestMethod] + public void SubtractSaturationClampTest() + { + Core.Color baseColor = HSVColor.Create(180, 0.5, 0.5); + Core.Color result = baseColor.SubtractHSV(saturation: 0.8); + + Assert.AreEqual(HSVColor.Create(180, 0, 0.5), result); + } + + [TestMethod] + public void SubtractValueTest() + { + Core.Color baseColor = HSVColor.Create(180, 0.5, 0.5); + Core.Color result = baseColor.SubtractHSV(value: 0.3); + + Assert.AreEqual(HSVColor.Create(180, 0.5, 0.2), result); + } + + [TestMethod] + public void SubtractValueClampTest() + { + Core.Color baseColor = HSVColor.Create(180, 0.5, 0.5); + Core.Color result = baseColor.SubtractHSV(value: .8); + + Assert.AreEqual(HSVColor.Create(180, 0.5, 0), result); + } + + #endregion + + #region Multiply + + [TestMethod] + public void MultiplyHueTest() + { + Core.Color baseColor = HSVColor.Create(180, 0.5, 0.5); + Core.Color result = baseColor.MultiplyHSV(hue: 1.5); + + Assert.AreEqual(HSVColor.Create(270, 0.5, 0.5), result); + } + + [TestMethod] + public void MultiplyHueWrapTest() + { + Core.Color baseColor = HSVColor.Create(180, 0.5, 0.5); + Core.Color result = baseColor.MultiplyHSV(hue: 3); + + Assert.AreEqual(HSVColor.Create(180, 0.5, 0.5), result); + } + + [TestMethod] + public void MultiplySaturationTest() + { + Core.Color baseColor = HSVColor.Create(180, 0.2, 0.2); + Core.Color result = baseColor.MultiplyHSV(saturation: 3); + + Assert.AreEqual(HSVColor.Create(180, 0.6, 0.2), result); + } + + [TestMethod] + public void MultiplySaturationClampTest() + { + Core.Color baseColor = HSVColor.Create(180, 0.5, 0.5); + Core.Color result = baseColor.MultiplyHSV(saturation: 3); + + Assert.AreEqual(HSVColor.Create(180, 1.0, 0.5), result); + } + + [TestMethod] + public void MultiplyValueTest() + { + Core.Color baseColor = HSVColor.Create(180, 0.2, 0.2); + Core.Color result = baseColor.MultiplyHSV(value: 3); + + Assert.AreEqual(HSVColor.Create(180, 0.2, 0.6), result); + } + + [TestMethod] + public void MultiplyValueClampTest() + { + Core.Color baseColor = HSVColor.Create(180, 0.5, 0.5); + Core.Color result = baseColor.MultiplyHSV(value: 3); + + Assert.AreEqual(HSVColor.Create(180, 0.5, 1.0), result); + } + + #endregion + + #region Divide + + [TestMethod] + public void DivideHueTest() + { + Core.Color baseColor = HSVColor.Create(180, 0.5, 0.5); + Core.Color result = baseColor.DivideHSV(hue: 30); + + Assert.AreEqual(HSVColor.Create(6, 0.5, 0.5), result); + } + + [TestMethod] + public void DivideSaturationTest() + { + Core.Color baseColor = HSVColor.Create(180, 0.6, 0.6); + Core.Color result = baseColor.DivideHSV(saturation: 2); + + Assert.AreEqual(HSVColor.Create(180, 0.3, 0.6), result); + } + + [TestMethod] + public void DivideValueTest() + { + Core.Color baseColor = HSVColor.Create(180, 0.6, 0.6); + Core.Color result = baseColor.DivideHSV(value: 2); + + Assert.AreEqual(HSVColor.Create(180, 0.6, 0.3), result); + } + + #endregion + + #region Set + + [TestMethod] + public void SetHueTest() + { + Core.Color baseColor = HSVColor.Create(180, 0.5, 0.5); + Core.Color result = baseColor.SetHSV(hue: 30); + + Assert.AreEqual(HSVColor.Create(30, 0.5, 0.5), result); + } + + [TestMethod] + public void SetHueWrapTest() + { + Core.Color baseColor = HSVColor.Create(180, 0.5, 0.5); + Core.Color result = baseColor.SetHSV(hue: 440); + + Assert.AreEqual(HSVColor.Create(80, 0.5, 0.5), result); + } + + [TestMethod] + public void SetHueWrapNegativeTest() + { + Core.Color baseColor = HSVColor.Create(180, 0.5, 0.5); + Core.Color result = baseColor.SetHSV(hue: -30); + + Assert.AreEqual(HSVColor.Create(330, 0.5, 0.5), result); + } + + [TestMethod] + public void SetSaturationTest() + { + Core.Color baseColor = HSVColor.Create(180, 0.5, 0.5); + Core.Color result = baseColor.SetHSV(saturation: 0.3); + + Assert.AreEqual(HSVColor.Create(180, 0.3, 0.5), result); + } + + [TestMethod] + public void SetSaturationClampTest() + { + Core.Color baseColor = HSVColor.Create(180, 0.5, 0.5); + Core.Color result = baseColor.SetHSV(saturation: 2); + + Assert.AreEqual(HSVColor.Create(180, 1.0, 0.5), result); + } + + [TestMethod] + public void SetValueTest() + { + Core.Color baseColor = HSVColor.Create(180, 0.5, 0.5); + Core.Color result = baseColor.SetHSV(value: 0.3); + + Assert.AreEqual(HSVColor.Create(180, 0.5, 0.3), result); + } + + [TestMethod] + public void SetValueClampTest() + { + Core.Color baseColor = HSVColor.Create(180, 0.5, 0.5); + Core.Color result = baseColor.SetHSV(value: 2); + + Assert.AreEqual(HSVColor.Create(180, 0.5, 1.0), result); + } + + #endregion + + #endregion + } +} diff --git a/Tests/RGB.NET.Core.Tests/Color/RGBColorTest.cs b/Tests/RGB.NET.Core.Tests/Color/RGBColorTest.cs new file mode 100644 index 0000000..2624444 --- /dev/null +++ b/Tests/RGB.NET.Core.Tests/Color/RGBColorTest.cs @@ -0,0 +1,432 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace RGB.NET.Core.Tests.Color +{ + [TestClass] + public class RGBColorTest + { + #region Manipulation + + #region Blend + + [TestMethod] + public void BlendOpaqueTest() + { + Core.Color baseColor = new Core.Color(255, 0, 0); + Core.Color blendColor = new Core.Color(0, 255, 0); + + Assert.AreEqual(blendColor, baseColor.Blend(blendColor)); + } + + [TestMethod] + public void BlendTransparentTest() + { + Core.Color baseColor = new Core.Color(255, 0, 0); + Core.Color blendColor = new Core.Color(0, 0, 255, 0); + + Assert.AreEqual(baseColor, baseColor.Blend(blendColor)); + } + + [TestMethod] + public void BlendUpTest() + { + Core.Color baseColor = new Core.Color(0.0, 0.0, 0.0); + Core.Color blendColor = new Core.Color(0.5, 1.0, 1.0, 1.0); + + Assert.AreEqual(new Core.Color(0.5, 0.5, 0.5), baseColor.Blend(blendColor)); + } + + [TestMethod] + public void BlendDownTest() + { + Core.Color baseColor = new Core.Color(1.0, 1.0, 1.0); + Core.Color blendColor = new Core.Color(0.5, 0.0, 0.0, 0.0); + + Assert.AreEqual(new Core.Color(0.5, 0.5, 0.5), baseColor.Blend(blendColor)); + } + + #endregion + + #region Add + + [TestMethod] + public void AddRGBTest() + { + Core.Color baseColor = new Core.Color(128, 128, 128, 128); + Core.Color result = baseColor.AddRGB(11, 12, 13); + + Assert.AreEqual(new Core.Color(128, 139, 140, 141), result); + } + + [TestMethod] + public void AddRGBPercentTest() + { + Core.Color baseColor = new Core.Color(0.5, 0.5, 0.5, 0.5); + Core.Color result = baseColor.AddRGB(0.2, 0.3, 0.4); + + Assert.AreEqual(new Core.Color(0.5, 0.7, 0.8, 0.9), result); + } + + [TestMethod] + public void AddATest() + { + Core.Color baseColor = new Core.Color(128, 128, 128, 128); + Core.Color result = baseColor.AddA(10); + + Assert.AreEqual(new Core.Color(138, 128, 128, 128), result); + } + + [TestMethod] + public void AddAPercentTest() + { + Core.Color baseColor = new Core.Color(0.5, 0.5, 0.5, 0.5); + Core.Color result = baseColor.AddA(0.1); + + Assert.AreEqual(new Core.Color(0.6, 0.5, 0.5, 0.5), result); + } + + [TestMethod] + public void AddRTest() + { + Core.Color baseColor = new Core.Color(128, 128, 128, 128); + Core.Color result = baseColor.AddRGB(r: 10); + + Assert.AreEqual(new Core.Color(128, 138, 128, 128), result); + } + + [TestMethod] + public void AddRPercentTest() + { + Core.Color baseColor = new Core.Color(0.5, 0.5, 0.5, 0.5); + Core.Color result = baseColor.AddRGB(r: 0.1); + + Assert.AreEqual(new Core.Color(0.5, 0.6, 0.5, 0.5), result); + } + + [TestMethod] + public void AddGTest() + { + Core.Color baseColor = new Core.Color(128, 128, 128, 128); + Core.Color result = baseColor.AddRGB(g: 10); + + Assert.AreEqual(new Core.Color(128, 128, 138, 128), result); + } + + [TestMethod] + public void AddGPercentTest() + { + Core.Color baseColor = new Core.Color(0.5, 0.5, 0.5, 0.5); + Core.Color result = baseColor.AddRGB(g: 0.1); + + Assert.AreEqual(new Core.Color(0.5, 0.5, 0.6, 0.5), result); + } + + [TestMethod] + public void AddBTest() + { + Core.Color baseColor = new Core.Color(128, 128, 128, 128); + Core.Color result = baseColor.AddRGB(b: 10); + + Assert.AreEqual(new Core.Color(128, 128, 128, 138), result); + } + + [TestMethod] + public void AddBPercentTest() + { + Core.Color baseColor = new Core.Color(0.5, 0.5, 0.5, 0.5); + Core.Color result = baseColor.AddRGB(b: 0.1); + + Assert.AreEqual(new Core.Color(0.5, 0.5, 0.5, 0.6), result); + } + + #endregion + + #region Substract + + [TestMethod] + public void SubtractRGBTest() + { + Core.Color baseColor = new Core.Color(128, 128, 128, 128); + Core.Color result = baseColor.SubtractRGB(11, 12, 13); + + Assert.AreEqual(new Core.Color(128, 117, 116, 115), result); + } + + [TestMethod] + public void SubtractRGBPercentTest() + { + Core.Color baseColor = new Core.Color(0.5, 0.5, 0.5, 0.5); + Core.Color result = baseColor.SubtractRGB(0.2, 0.3, 0.4); + + Assert.AreEqual(new Core.Color(0.5, 0.3, 0.2, 0.1), result); + } + + [TestMethod] + public void SubtractATest() + { + Core.Color baseColor = new Core.Color(128, 128, 128, 128); + Core.Color result = baseColor.SubtractA(10); + + Assert.AreEqual(new Core.Color(118, 128, 128, 128), result); + } + + [TestMethod] + public void SubtractAPercentTest() + { + Core.Color baseColor = new Core.Color(0.5, 0.5, 0.5, 0.5); + Core.Color result = baseColor.SubtractA(0.1); + + Assert.AreEqual(new Core.Color(0.4, 0.5, 0.5, 0.5), result); + } + + [TestMethod] + public void SubtractRTest() + { + Core.Color baseColor = new Core.Color(128, 128, 128, 128); + Core.Color result = baseColor.SubtractRGB(r: 10); + + Assert.AreEqual(new Core.Color(128, 118, 128, 128), result); + } + + [TestMethod] + public void SubtractRPercentTest() + { + Core.Color baseColor = new Core.Color(0.5, 0.5, 0.5, 0.5); + Core.Color result = baseColor.SubtractRGB(r: 0.1); + + Assert.AreEqual(new Core.Color(0.5, 0.4, 0.5, 0.5), result); + } + + [TestMethod] + public void SubtractGTest() + { + Core.Color baseColor = new Core.Color(128, 128, 128, 128); + Core.Color result = baseColor.SubtractRGB(g: 10); + + Assert.AreEqual(new Core.Color(128, 128, 118, 128), result); + } + + [TestMethod] + public void SubtractGPercentTest() + { + Core.Color baseColor = new Core.Color(0.5, 0.5, 0.5, 0.5); + Core.Color result = baseColor.SubtractRGB(g: 0.1); + + Assert.AreEqual(new Core.Color(0.5, 0.5, 0.4, 0.5), result); + } + + [TestMethod] + public void SubtractBTest() + { + Core.Color baseColor = new Core.Color(128, 128, 128, 128); + Core.Color result = baseColor.SubtractRGB(b: 10); + + Assert.AreEqual(new Core.Color(128, 128, 128, 118), result); + } + + [TestMethod] + public void SubtractBPercentTest() + { + Core.Color baseColor = new Core.Color(0.5, 0.5, 0.5, 0.5); + Core.Color result = baseColor.SubtractRGB(b: 0.1); + + Assert.AreEqual(new Core.Color(0.5, 0.5, 0.5, 0.4), result); + } + + #endregion + + #region Multiply + + [TestMethod] + public void MultiplyRGBPercentTest() + { + Core.Color baseColor = new Core.Color(0.2, 0.2, 0.2, 0.2); + Core.Color result = baseColor.MultiplyRGB(3, 4, 5); + + Assert.AreEqual(new Core.Color(0.2, 0.6, 0.8, 1.0), result); + } + + [TestMethod] + public void MultiplyAPercentTest() + { + Core.Color baseColor = new Core.Color(0.2, 0.2, 0.2, 0.2); + Core.Color result = baseColor.MultiplyA(3); + + Assert.AreEqual(new Core.Color(0.6, 0.2, 0.2, 0.2), result); + } + + [TestMethod] + public void MultiplyRPercentTest() + { + Core.Color baseColor = new Core.Color(0.2, 0.2, 0.2, 0.2); + Core.Color result = baseColor.MultiplyRGB(r: 3); + + Assert.AreEqual(new Core.Color(0.2, 0.6, 0.2, 0.2), result); + } + + [TestMethod] + public void MultiplyGPercentTest() + { + Core.Color baseColor = new Core.Color(0.2, 0.2, 0.2, 0.2); + Core.Color result = baseColor.MultiplyRGB(g: 3); + + Assert.AreEqual(new Core.Color(0.2, 0.2, 0.6, 0.2), result); + } + + [TestMethod] + public void MultiplyBPercentTest() + { + Core.Color baseColor = new Core.Color(0.2, 0.2, 0.2, 0.2); + Core.Color result = baseColor.MultiplyRGB(b: 3); + + Assert.AreEqual(new Core.Color(0.2, 0.2, 0.2, 0.6), result); + } + + #endregion + + #region Divide + + [TestMethod] + public void DivideRGBPercentTest() + { + Core.Color baseColor = new Core.Color(0.2, 0.6, 0.8, 1.0); + Core.Color result = baseColor.DivideRGB(3, 4, 5); + + Assert.AreEqual(new Core.Color(0.2, 0.2, 0.2, 0.2), result); + } + + [TestMethod] + public void DivideAPercentTest() + { + Core.Color baseColor = new Core.Color(0.6, 0.2, 0.2, 0.2); + Core.Color result = baseColor.DivideA(3); + + Assert.AreEqual(new Core.Color(0.2, 0.2, 0.2, 0.2), result); + } + + [TestMethod] + public void DivideRPercentTest() + { + Core.Color baseColor = new Core.Color(0.2, 0.6, 0.2, 0.2); + Core.Color result = baseColor.DivideRGB(r: 3); + + Assert.AreEqual(new Core.Color(0.2, 0.2, 0.2, 0.2), result); + } + + [TestMethod] + public void DivideGPercentTest() + { + Core.Color baseColor = new Core.Color(0.2, 0.2, 0.6, 0.2); + Core.Color result = baseColor.DivideRGB(g: 3); + + Assert.AreEqual(new Core.Color(0.2, 0.2, 0.2, 0.2), result); + } + + [TestMethod] + public void DivideBPercentTest() + { + Core.Color baseColor = new Core.Color(0.2, 0.2, 0.2, 0.6); + Core.Color result = baseColor.DivideRGB(b: 3); + + Assert.AreEqual(new Core.Color(0.2, 0.2, 0.2, 0.2), result); + } + + #endregion + + #region Set + + [TestMethod] + public void SetRGBTest() + { + Core.Color baseColor = new Core.Color(128, 128, 128, 128); + Core.Color result = baseColor.SetRGB(11, 12, 13); + + Assert.AreEqual(new Core.Color(128, 11, 12, 13), result); + } + + [TestMethod] + public void SetRGBPercentTest() + { + Core.Color baseColor = new Core.Color(0.5, 0.5, 0.5, 0.5); + Core.Color result = baseColor.SetRGB(0.2, 0.3, 0.4); + + Assert.AreEqual(new Core.Color(0.5, 0.2, 0.3, 0.4), result); + } + + [TestMethod] + public void SetATest() + { + Core.Color baseColor = new Core.Color(128, 128, 128, 128); + Core.Color result = baseColor.SetA(10); + + Assert.AreEqual(new Core.Color(10, 128, 128, 128), result); + } + + [TestMethod] + public void SetAPercentTest() + { + Core.Color baseColor = new Core.Color(0.5, 0.5, 0.5, 0.5); + Core.Color result = baseColor.SetA(0.1); + + Assert.AreEqual(new Core.Color(0.1, 0.5, 0.5, 0.5), result); + } + + [TestMethod] + public void SetRTest() + { + Core.Color baseColor = new Core.Color(128, 128, 128, 128); + Core.Color result = baseColor.SetRGB(r: 10); + + Assert.AreEqual(new Core.Color(128, 10, 128, 128), result); + } + + [TestMethod] + public void SetRPercentTest() + { + Core.Color baseColor = new Core.Color(0.5, 0.5, 0.5, 0.5); + Core.Color result = baseColor.SetRGB(r: 0.1); + + Assert.AreEqual(new Core.Color(0.5, 0.1, 0.5, 0.5), result); + } + + [TestMethod] + public void SetGTest() + { + Core.Color baseColor = new Core.Color(128, 128, 128, 128); + Core.Color result = baseColor.SetRGB(g: 10); + + Assert.AreEqual(new Core.Color(128, 128, 10, 128), result); + } + + [TestMethod] + public void SetGPercentTest() + { + Core.Color baseColor = new Core.Color(0.5, 0.5, 0.5, 0.5); + Core.Color result = baseColor.SetRGB(g: 0.1); + + Assert.AreEqual(new Core.Color(0.5, 0.5, 0.1, 0.5), result); + } + + [TestMethod] + public void SetBTest() + { + Core.Color baseColor = new Core.Color(128, 128, 128, 128); + Core.Color result = baseColor.SetRGB(b: 10); + + Assert.AreEqual(new Core.Color(128, 128, 128, 10), result); + } + + [TestMethod] + public void SetBPercentTest() + { + Core.Color baseColor = new Core.Color(0.5, 0.5, 0.5, 0.5); + Core.Color result = baseColor.SetRGB(b: 0.1); + + Assert.AreEqual(new Core.Color(0.5, 0.5, 0.5, 0.1), result); + } + + #endregion + + #endregion + } +} diff --git a/Tests/RGB.NET.Core.Tests/RGB.NET.Core.Tests.csproj b/Tests/RGB.NET.Core.Tests/RGB.NET.Core.Tests.csproj new file mode 100644 index 0000000..49bf8e9 --- /dev/null +++ b/Tests/RGB.NET.Core.Tests/RGB.NET.Core.Tests.csproj @@ -0,0 +1,19 @@ + + + + netcoreapp2.2 + + false + + + + + + + + + + + + +