| File: System\Windows\Forms\Rendering\ControlPaint.HLSColor.cs | Web Access |
| Project: src\winforms\src\System.Windows.Forms\System.Windows.Forms.csproj (System.Windows.Forms) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Drawing; namespace System.Windows.Forms; public static partial class ControlPaint { /// <summary> /// Logic copied from Windows sources to copy the lightening and darkening of colors. /// </summary> private readonly struct HLSColor : IEquatable<HLSColor> { private const int ShadowAdjustment = -333; private const int HighlightAdjustment = 500; private const int Range = 240; private const int HLSMax = Range; private const int RGBMax = 255; private const int Undefined = HLSMax * 2 / 3; private readonly int _hue; private readonly int _saturation; private readonly bool _isSystemColors_Control; public HLSColor(Color color) { _isSystemColors_Control = color.ToKnownColor() == SystemColors.Control.ToKnownColor(); ARGB argb = color; int r = argb.R; int g = argb.G; int b = argb.B; int Rdelta, Gdelta, Bdelta; // intermediate value: % of spread from max // calculate lightness int max = Math.Max(Math.Max(r, g), b); int min = Math.Min(Math.Min(r, g), b); int sum = max + min; Luminosity = ((sum * HLSMax) + RGBMax) / (2 * RGBMax); int dif = max - min; if (dif == 0) { // r=g=b --> achromatic case _saturation = 0; _hue = Undefined; } else { // chromatic case _saturation = Luminosity <= (HLSMax / 2) ? ((dif * HLSMax) + (sum / 2)) / sum : ((dif * HLSMax) + (2 * RGBMax - sum) / 2) / (2 * RGBMax - sum); Rdelta = (((max - r) * (HLSMax / 6)) + (dif / 2)) / dif; Gdelta = (((max - g) * (HLSMax / 6)) + (dif / 2)) / dif; Bdelta = (((max - b) * (HLSMax / 6)) + (dif / 2)) / dif; if (r == max) { _hue = Bdelta - Gdelta; } else if (g == max) { _hue = (HLSMax / 3) + Rdelta - Bdelta; } else { // B == cMax _hue = (2 * HLSMax / 3) + Gdelta - Rdelta; } if (_hue < 0) { _hue += HLSMax; } if (_hue > HLSMax) { _hue -= HLSMax; } } } public int Luminosity { get; } public Color Darker(float percDarker) { if (!_isSystemColors_Control) { int zeroLum = NewLuma(ShadowAdjustment, true); return ColorFromHLS(_hue, zeroLum - (int)(zeroLum * percDarker), _saturation); } else { // With the usual color scheme, ControlDark/DarkDark is not exactly // what we would otherwise calculate if (percDarker == 0.0f) { return SystemColors.ControlDark; } else if (percDarker == 1.0f) { return SystemColors.ControlDarkDark; } else { ARGB dark = SystemColors.ControlDark; ARGB darkDark = SystemColors.ControlDarkDark; return Color.FromArgb( (byte)(dark.R - (byte)((dark.R - darkDark.R) * percDarker)), (byte)(dark.G - (byte)((dark.G - darkDark.G) * percDarker)), (byte)(dark.B - (byte)((dark.B - darkDark.B) * percDarker))); } } } public override bool Equals(object? o) { if (o is not HLSColor hlsColor) { return false; } return Equals(hlsColor); } public bool Equals(HLSColor other) { return _hue == other._hue && _saturation == other._saturation && Luminosity == other.Luminosity && _isSystemColors_Control == other._isSystemColors_Control; } public static bool operator ==(HLSColor a, HLSColor b) => a.Equals(b); public static bool operator !=(HLSColor a, HLSColor b) => !a.Equals(b); public override int GetHashCode() => HashCode.Combine(_hue, _saturation, Luminosity); public Color Lighter(float percentLighter) { if (_isSystemColors_Control) { // With the usual color scheme, ControlLight/LightLight is not exactly // what we would otherwise calculate if (percentLighter == 0.0f) { return SystemColors.ControlLight; } else if (percentLighter == 1.0f) { return SystemColors.ControlLightLight; } else { ARGB light = SystemColors.ControlLight; ARGB lightLight = SystemColors.ControlLightLight; return Color.FromArgb( (byte)(light.R - (byte)((light.R - lightLight.R) * percentLighter)), (byte)(light.G - (byte)((light.G - lightLight.G) * percentLighter)), (byte)(light.B - (byte)((light.B - lightLight.B) * percentLighter))); } } else { int zeroLum = Luminosity; int oneLum = NewLuma(HighlightAdjustment, true); return ColorFromHLS(_hue, zeroLum + (int)((oneLum - zeroLum) * percentLighter), _saturation); } } private int NewLuma(int n, bool scale) { return NewLuma(Luminosity, n, scale); } private static int NewLuma(int luminosity, int n, bool scale) { if (n == 0) { return luminosity; } if (scale) { return n > 0 ? (int)((luminosity * (1000 - n) + (Range + 1L) * n) / 1000) : luminosity * (n + 1000) / 1000; } luminosity += (int)((long)n * Range / 1000); if (luminosity < 0) { return 0; } else if (luminosity > HLSMax) { return HLSMax; } return luminosity; } private static Color ColorFromHLS(int hue, int luminosity, int saturation) { byte r, g, b; int magic1, magic2; if (saturation == 0) { // achromatic case r = g = b = (byte)(luminosity * RGBMax / HLSMax); if (hue != Undefined) { /* ERROR */ } } else { // chromatic case if (luminosity <= (HLSMax / 2)) { magic2 = ((luminosity * (HLSMax + saturation)) + (HLSMax / 2)) / HLSMax; } else { magic2 = luminosity + saturation - (((luminosity * saturation) + (HLSMax / 2)) / HLSMax); } magic1 = 2 * luminosity - magic2; // get RGB, change units from HLSMax to RGBMax r = (byte)((HueToRGB(magic1, magic2, hue + HLSMax / 3) * RGBMax + (HLSMax / 2)) / HLSMax); g = (byte)((HueToRGB(magic1, magic2, hue) * RGBMax + (HLSMax / 2)) / HLSMax); b = (byte)((HueToRGB(magic1, magic2, hue - HLSMax / 3) * RGBMax + (HLSMax / 2)) / HLSMax); } return Color.FromArgb(r, g, b); } private static int HueToRGB(int n1, int n2, int hue) { // range check: note values passed add/subtract thirds of range /* The following is redundant for WORD (unsigned int) */ if (hue < 0) { hue += HLSMax; } if (hue > HLSMax) { hue -= HLSMax; } // return r, g, or b value from this sector if (hue < (HLSMax / 6)) { return n1 + (((n2 - n1) * hue + (HLSMax / 12)) / (HLSMax / 6)); } if (hue < (HLSMax / 2)) { return n2; } if (hue < (HLSMax * 2 / 3)) { return n1 + (((n2 - n1) * ((HLSMax * 2 / 3) - hue) + (HLSMax / 12)) / (HLSMax / 6)); } else { return n1; } } } }