Browse Source

Add new HslColor struct

pull/7951/head
robloo 4 years ago
parent
commit
1c019eb211
  1. 3
      src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
  2. 12
      src/Avalonia.Visuals/Media/Color.cs
  3. 322
      src/Avalonia.Visuals/Media/HslColor.cs
  4. 16
      src/Avalonia.Visuals/Media/HsvColor.cs

3
src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj

@ -83,6 +83,9 @@
<Compile Include="../Avalonia.Visuals/Media/Color.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="../Avalonia.Visuals/Media/HslColor.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="../Avalonia.Visuals/Media/HsvColor.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>

12
src/Avalonia.Visuals/Media/Color.cs

@ -308,11 +308,23 @@ namespace Avalonia.Media
}
}
/// <summary>
/// Indicates whether the values of two specified <see cref="Color"/> objects are equal.
/// </summary>
/// <param name="left">The first object to compare.</param>
/// <param name="right">The second object to compare.</param>
/// <returns>True if left and right are equal; otherwise, false.</returns>
public static bool operator ==(Color left, Color right)
{
return left.Equals(right);
}
/// <summary>
/// Indicates whether the values of two specified <see cref="Color"/> objects are not equal.
/// </summary>
/// <param name="left">The first object to compare.</param>
/// <param name="right">The second object to compare.</param>
/// <returns>True if left and right are not equal; otherwise, false.</returns>
public static bool operator !=(Color left, Color right)
{
return !left.Equals(right);

322
src/Avalonia.Visuals/Media/HslColor.cs

@ -0,0 +1,322 @@
// Color conversion portions of this source file are adapted from the Windows Community Toolkit project.
// (https://github.com/CommunityToolkit/WindowsCommunityToolkit)
//
// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
using System;
using System.Globalization;
using System.Text;
using Avalonia.Utilities;
namespace Avalonia.Media
{
/// <summary>
/// Defines a color using the hue/saturation/lightness (HSL) model.
/// </summary>
#if !BUILDTASK
public
#endif
readonly struct HslColor : IEquatable<HslColor>
{
/// <summary>
/// Initializes a new instance of the <see cref="HslColor"/> struct.
/// </summary>
/// <param name="alpha">The Alpha (transparency) component in the range from 0..1.</param>
/// <param name="hue">The Hue component in the range from 0..360.
/// Note that 360 is equivalent to 0 and will be adjusted automatically.</param>
/// <param name="saturation">The Saturation component in the range from 0..1.</param>
/// <param name="lightness">The Lightness component in the range from 0..1.</param>
public HslColor(
double alpha,
double hue,
double saturation,
double lightness)
{
A = MathUtilities.Clamp(alpha, 0.0, 1.0);
H = MathUtilities.Clamp(hue, 0.0, 360.0);
S = MathUtilities.Clamp(saturation, 0.0, 1.0);
L = MathUtilities.Clamp(lightness, 0.0, 1.0);
// The maximum value of Hue is technically 360 minus epsilon (just below 360).
// This is because, in a color circle, 360 degrees is equivalent to 0 degrees.
// However, that is too tricky to work with in code and isn't as intuitive.
// Therefore, since 360 == 0, just wrap 360 if needed back to 0.
H = (H == 360.0 ? 0 : H);
}
/// <summary>
/// Initializes a new instance of the <see cref="HslColor"/> struct.
/// </summary>
/// <remarks>
/// This constructor exists only for internal use where performance is critical.
/// Whether or not the component values are in the correct ranges must be known.
/// </remarks>
/// <param name="alpha">The Alpha (transparency) component in the range from 0..1.</param>
/// <param name="hue">The Hue component in the range from 0..360.
/// Note that 360 is equivalent to 0 and will be adjusted automatically.</param>
/// <param name="saturation">The Saturation component in the range from 0..1.</param>
/// <param name="lightness">The Lightness component in the range from 0..1.</param>
/// <param name="clampValues">Whether to clamp component values to their required ranges.</param>
internal HslColor(
double alpha,
double hue,
double saturation,
double lightness,
bool clampValues)
{
if (clampValues)
{
A = MathUtilities.Clamp(alpha, 0.0, 1.0);
H = MathUtilities.Clamp(hue, 0.0, 360.0);
S = MathUtilities.Clamp(saturation, 0.0, 1.0);
L = MathUtilities.Clamp(lightness, 0.0, 1.0);
// See comments in constructor above
H = (H == 360.0 ? 0 : H);
}
else
{
A = alpha;
H = hue;
S = saturation;
L = lightness;
}
}
/// <summary>
/// Gets the Alpha (transparency) component in the range from 0..1.
/// </summary>
public double A { get; }
/// <summary>
/// Gets the Hue component in the range from 0..360.
/// Note that 360 is equivalent to 0 and will be adjusted automatically.
/// </summary>
public double H { get; }
/// <summary>
/// Gets the Saturation component in the range from 0..1.
/// </summary>
public double S { get; }
/// <summary>
/// Gets the Lightness component in the range from 0..1.
/// </summary>
public double L { get; }
/// <inheritdoc/>
public bool Equals(HslColor other)
{
return other.A == A &&
other.H == H &&
other.S == S &&
other.L == L;
}
/// <inheritdoc/>
public override bool Equals(object? obj)
{
if (obj is HslColor hslColor)
{
return Equals(hslColor);
}
else
{
return false;
}
}
/// <summary>
/// Gets a hashcode for this object.
/// Hashcode is not guaranteed to be unique.
/// </summary>
/// <returns>The hashcode for this object.</returns>
public override int GetHashCode()
{
// Same algorithm as Color
// This is used instead of HashCode.Combine() due to .NET Standard 2.0 requirements
unchecked
{
int hashCode = A.GetHashCode();
hashCode = (hashCode * 397) ^ H.GetHashCode();
hashCode = (hashCode * 397) ^ S.GetHashCode();
hashCode = (hashCode * 397) ^ L.GetHashCode();
return hashCode;
}
}
/// <summary>
/// Returns the RGB color model equivalent of this HSL color.
/// </summary>
/// <returns>The RGB equivalent color.</returns>
public Color ToRgb()
{
// Use the by-component conversion method directly for performance
return HslColor.ToRgb(H, S, L, A);
}
/// <summary>
/// Creates a new <see cref="HslColor"/> from individual color component values.
/// </summary>
/// <remarks>
/// This exists for symmetry with the <see cref="Color"/> struct; however, the
/// appropriate constructor should commonly be used instead.
/// </remarks>
/// <param name="a">The Alpha (transparency) component in the range from 0..1.</param>
/// <param name="h">The Hue component in the range from 0..360.</param>
/// <param name="s">The Saturation component in the range from 0..1.</param>
/// <param name="l">The Lightness component in the range from 0..1.</param>
/// <returns>A new <see cref="HslColor"/> built from the individual color component values.</returns>
public static HslColor FromAhsl(double a, double h, double s, double l)
{
return new HslColor(a, h, s, l);
}
/// <summary>
/// Creates a new <see cref="HslColor"/> from individual color component values.
/// </summary>
/// <remarks>
/// This exists for symmetry with the <see cref="Color"/> struct; however, the
/// appropriate constructor should commonly be used instead.
/// </remarks>
/// <param name="h">The Hue component in the range from 0..360.</param>
/// <param name="s">The Saturation component in the range from 0..1.</param>
/// <param name="l">The Lightness component in the range from 0..1.</param>
/// <returns>A new <see cref="HslColor"/> built from the individual color component values.</returns>
public static HslColor FromHsl(double h, double s, double l)
{
return new HslColor(1.0, h, s, l);
}
/// <summary>
/// Converts the given HSL color to it's RGB color equivalent.
/// </summary>
/// <param name="hslColor">The color in the HSL color model.</param>
/// <returns>A new RGB <see cref="Color"/> equivalent to the given HSLA values.</returns>
public static Color ToRgb(HslColor hslColor)
{
return HslColor.ToRgb(hslColor.H, hslColor.S, hslColor.L, hslColor.A);
}
/// <summary>
/// Converts the given HSLA color component values to it's RGB color equivalent.
/// </summary>
/// <param name="hue">The Hue component in the HSL color model in the range from 0..360.</param>
/// <param name="saturation">The Saturation component in the HSL color model in the range from 0..1.</param>
/// <param name="lightness">The Lightness component in the HSL color model in the range from 0..1.</param>
/// <param name="alpha">The Alpha component in the range from 0..1.</param>
/// <returns>A new RGB <see cref="Color"/> equivalent to the given HSLA values.</returns>
public static Color ToRgb(
double hue,
double saturation,
double lightness,
double alpha = 1.0)
{
// Note: Conversion code is originally based on ColorHelper in the Windows Community Toolkit (licensed MIT)
// https://github.com/CommunityToolkit/WindowsCommunityToolkit/blob/main/Microsoft.Toolkit.Uwp/Helpers/ColorHelper.cs
// It has been modified to ensure input ranges and not throw exceptions.
// We want the hue to be between 0 and 359,
// so we first ensure that that's the case.
while (hue >= 360.0)
{
hue -= 360.0;
}
while (hue < 0.0)
{
hue += 360.0;
}
// We similarly clamp saturation, lightness and alpha between 0 and 1.
saturation = saturation < 0.0 ? 0.0 : saturation;
saturation = saturation > 1.0 ? 1.0 : saturation;
lightness = lightness < 0.0 ? 0.0 : lightness;
lightness = lightness > 1.0 ? 1.0 : lightness;
alpha = alpha < 0.0 ? 0.0 : alpha;
alpha = alpha > 1.0 ? 1.0 : alpha;
double chroma = (1 - Math.Abs((2 * lightness) - 1)) * saturation;
double h1 = hue / 60;
double x = chroma * (1 - Math.Abs((h1 % 2) - 1));
double m = lightness - (0.5 * chroma);
double r1, g1, b1;
if (h1 < 1)
{
r1 = chroma;
g1 = x;
b1 = 0;
}
else if (h1 < 2)
{
r1 = x;
g1 = chroma;
b1 = 0;
}
else if (h1 < 3)
{
r1 = 0;
g1 = chroma;
b1 = x;
}
else if (h1 < 4)
{
r1 = 0;
g1 = x;
b1 = chroma;
}
else if (h1 < 5)
{
r1 = x;
g1 = 0;
b1 = chroma;
}
else
{
r1 = chroma;
g1 = 0;
b1 = x;
}
return Color.FromArgb(
(byte)(255 * alpha),
(byte)(255 * (r1 + m)),
(byte)(255 * (g1 + m)),
(byte)(255 * (b1 + m)));
}
/// <summary>
/// Indicates whether the values of two specified <see cref="HslColor"/> objects are equal.
/// </summary>
/// <param name="left">The first object to compare.</param>
/// <param name="right">The second object to compare.</param>
/// <returns>True if left and right are equal; otherwise, false.</returns>
public static bool operator ==(HslColor left, HslColor right)
{
return left.Equals(right);
}
/// <summary>
/// Indicates whether the values of two specified <see cref="HslColor"/> objects are not equal.
/// </summary>
/// <param name="left">The first object to compare.</param>
/// <param name="right">The second object to compare.</param>
/// <returns>True if left and right are not equal; otherwise, false.</returns>
public static bool operator !=(HslColor left, HslColor right)
{
return !(left == right);
}
/// <summary>
/// Explicit conversion from an <see cref="HslColor"/> to a <see cref="Color"/>.
/// </summary>
/// <param name="hslColor">The <see cref="HslColor"/> to convert.</param>
public static explicit operator Color(HslColor hslColor)
{
return hslColor.ToRgb();
}
}
}

16
src/Avalonia.Visuals/Media/HsvColor.cs

@ -338,10 +338,10 @@ namespace Avalonia.Media
/// <summary>
/// Converts the given HSVA color component values to it's RGB color equivalent.
/// </summary>
/// <param name="hue">The hue component in the HSV color model in the range from 0..360.</param>
/// <param name="saturation">The saturation component in the HSV color model in the range from 0..1.</param>
/// <param name="value">The value component in the HSV color model in the range from 0..1.</param>
/// <param name="alpha">The alpha component in the range from 0..1.</param>
/// <param name="hue">The Hue component in the HSV color model in the range from 0..360.</param>
/// <param name="saturation">The Saturation component in the HSV color model in the range from 0..1.</param>
/// <param name="value">The Value component in the HSV color model in the range from 0..1.</param>
/// <param name="alpha">The Alpha component in the range from 0..1.</param>
/// <returns>A new RGB <see cref="Color"/> equivalent to the given HSVA values.</returns>
public static Color ToRgb(
double hue,
@ -467,10 +467,10 @@ namespace Avalonia.Media
}
return Color.FromArgb(
(byte)Math.Round(alpha * 255),
(byte)Math.Round(r * 255),
(byte)Math.Round(g * 255),
(byte)Math.Round(b * 255));
(byte)(alpha * 255),
(byte)(r * 255),
(byte)(g * 255),
(byte)(b * 255));
}
/// <summary>

Loading…
Cancel
Save