動作確認環境:C# 9.0(.NET 5)
WinAPIを使ったHLS色構造体のコードです。Color
型およびタプル(Tuple<ushort, ushort, ushort>
およびValueTuple<ushort, ushort, ushort>
)との相互変換をサポートしています。Color
とは異なりひとつの整数で表すことを前程としないため、IComparable
およびIComparable<T>
は実装しません。同様の理由からColor
との明示的なキャストをサポートしません。
C# 9.0機能の練習も兼ねています。
using System; using System.Drawing; using System.Runtime.InteropServices; using System.Runtime.Serialization; var hls = new HLSColor(0, 100, 20); var rgb = hls.ToColor(); hls += new HLSColor(2, 2, 2); public struct HLSColor : IEquatable<HLSColor>, ISerializable { public ushort H; public ushort L; public ushort S; #region コンストラクタ public HLSColor(ushort h, ushort l, ushort s) => (H, L, S) = (h, l, s); public HLSColor(Tuple<ushort, ushort, ushort> value) => (H, L, S) = (value.Item1, value.Item2, value.Item3); public HLSColor(in ValueTuple<ushort, ushort, ushort> value) => (H, L, S) = (value.Item1, value.Item2, value.Item3); public HLSColor(SerializationInfo info, StreamingContext context) => (H, L, S) = (info.GetUInt16("H"), info.GetUInt16("L"), info.GetUInt16("S")); #endregion #region 一般の比較・文字列化関数 public bool Equals(HLSColor other) => H == other.H && L == other.L && S == other.S; public override bool Equals(object obj) => obj is HLSColor hls2 && Equals(hls2); public override int GetHashCode() => ToValueTuple().GetHashCode(); public override string ToString() => string.Format("HLS=({0},{1},{2})", H, L, S); #endregion #region Colorとの相互変換関数 public static HLSColor FromColor(Color color) { NativeMethods.ColorRGBToHLS(unchecked((uint)color.ToArgb()), out var h, out var l, out var s); return new HLSColor(h, l, s); } public Color ToColor() => Color.FromArgb(unchecked((int)NativeMethods.ColorHLSToRGB(H, L, S))); #endregion #region シリアル化 public void GetObjectData(SerializationInfo info, StreamingContext context) { if (info == null) throw new ArgumentNullException(nameof(info)); info.AddValue("H", H); info.AddValue("L", L); info.AddValue("S", S); } #endregion #region 比較演算子 public static bool operator ==(HLSColor v1, HLSColor v2) => v1.Equals(v2); public static bool operator !=(HLSColor v1, HLSColor v2) => !v1.Equals(v2); /// <summary>加算。和がushortの最大値超過の場合は0xffffに抑えます。</summary> public static HLSColor operator +(HLSColor v1, HLSColor v2) => new HLSColor(ClampAddUInt16(v1.H, v2.H), ClampAddUInt16(v1.L, v2.L), ClampAddUInt16(v1.S, v2.S)); /// <summary>減算。差がushortの最小値未満の場合は0に抑えます。</summary> public static HLSColor operator -(HLSColor v1, HLSColor v2) => new HLSColor(ClampDiffUInt16(v1.H, v2.H), ClampDiffUInt16(v1.L, v2.L), ClampDiffUInt16(v1.S, v2.S)); #endregion #region タプルとの相互変換 public ValueTuple<ushort, ushort, ushort> ToValueTuple() => ValueTuple.Create(H, S, L); public Tuple<ushort, ushort, ushort> ToTuple() => Tuple.Create(H, S, L); public static explicit operator Tuple<ushort, ushort, ushort>(HLSColor value) => value.ToTuple(); public static explicit operator ValueTuple<ushort, ushort, ushort>(HLSColor value) => value.ToValueTuple(); public static explicit operator HLSColor(Tuple<ushort, ushort, ushort> value) => new HLSColor(value); public static explicit operator HLSColor(ValueTuple<ushort, ushort, ushort> value) => new HLSColor(value); #endregion #region 補助関数 private static ushort ClampAddUInt16(ushort x, ushort y) => (x + y) switch { int z when z > 0xffffu => (ushort)0xffffu, int z => (ushort)z }; private static ushort ClampDiffUInt16(ushort x, ushort y) => (x - y) switch { int z when z > 0 => (ushort)z, _ => 0 }; #endregion private static class NativeMethods { [DllImport("shlwapi.dll")] public static extern uint ColorHLSToRGB(ushort wHue, ushort wLuminance, ushort wSaturation); [DllImport("shlwapi.dll")] public static extern void ColorRGBToHLS(uint clrRGB, out ushort pwHue, out ushort pwLuminance, out ushort pwSaturation); } }