File: System\Drawing\Color.cs
Web Access
Project: src\src\libraries\System.Drawing.Primitives\src\System.Drawing.Primitives.csproj (System.Drawing.Primitives)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
 
namespace System.Drawing
{
    [DebuggerDisplay("{NameAndARGBValue}")]
    [Editor("System.Drawing.Design.ColorEditor, System.Drawing.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a",
            "System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
    [Serializable]
    [TypeConverter("System.Drawing.ColorConverter, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
    [TypeForwardedFrom("System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
    public readonly struct Color : IEquatable<Color>
    {
        public static readonly Color Empty;
 
        // -------------------------------------------------------------------
        //  static list of "web" colors...
        //
        public static Color Transparent => new Color(KnownColor.Transparent);
 
        public static Color AliceBlue => new Color(KnownColor.AliceBlue);
 
        public static Color AntiqueWhite => new Color(KnownColor.AntiqueWhite);
 
        public static Color Aqua => new Color(KnownColor.Aqua);
 
        public static Color Aquamarine => new Color(KnownColor.Aquamarine);
 
        public static Color Azure => new Color(KnownColor.Azure);
 
        public static Color Beige => new Color(KnownColor.Beige);
 
        public static Color Bisque => new Color(KnownColor.Bisque);
 
        public static Color Black => new Color(KnownColor.Black);
 
        public static Color BlanchedAlmond => new Color(KnownColor.BlanchedAlmond);
 
        public static Color Blue => new Color(KnownColor.Blue);
 
        public static Color BlueViolet => new Color(KnownColor.BlueViolet);
 
        public static Color Brown => new Color(KnownColor.Brown);
 
        public static Color BurlyWood => new Color(KnownColor.BurlyWood);
 
        public static Color CadetBlue => new Color(KnownColor.CadetBlue);
 
        public static Color Chartreuse => new Color(KnownColor.Chartreuse);
 
        public static Color Chocolate => new Color(KnownColor.Chocolate);
 
        public static Color Coral => new Color(KnownColor.Coral);
 
        public static Color CornflowerBlue => new Color(KnownColor.CornflowerBlue);
 
        public static Color Cornsilk => new Color(KnownColor.Cornsilk);
 
        public static Color Crimson => new Color(KnownColor.Crimson);
 
        public static Color Cyan => new Color(KnownColor.Cyan);
 
        public static Color DarkBlue => new Color(KnownColor.DarkBlue);
 
        public static Color DarkCyan => new Color(KnownColor.DarkCyan);
 
        public static Color DarkGoldenrod => new Color(KnownColor.DarkGoldenrod);
 
        public static Color DarkGray => new Color(KnownColor.DarkGray);
 
        public static Color DarkGreen => new Color(KnownColor.DarkGreen);
 
        public static Color DarkKhaki => new Color(KnownColor.DarkKhaki);
 
        public static Color DarkMagenta => new Color(KnownColor.DarkMagenta);
 
        public static Color DarkOliveGreen => new Color(KnownColor.DarkOliveGreen);
 
        public static Color DarkOrange => new Color(KnownColor.DarkOrange);
 
        public static Color DarkOrchid => new Color(KnownColor.DarkOrchid);
 
        public static Color DarkRed => new Color(KnownColor.DarkRed);
 
        public static Color DarkSalmon => new Color(KnownColor.DarkSalmon);
 
        public static Color DarkSeaGreen => new Color(KnownColor.DarkSeaGreen);
 
        public static Color DarkSlateBlue => new Color(KnownColor.DarkSlateBlue);
 
        public static Color DarkSlateGray => new Color(KnownColor.DarkSlateGray);
 
        public static Color DarkTurquoise => new Color(KnownColor.DarkTurquoise);
 
        public static Color DarkViolet => new Color(KnownColor.DarkViolet);
 
        public static Color DeepPink => new Color(KnownColor.DeepPink);
 
        public static Color DeepSkyBlue => new Color(KnownColor.DeepSkyBlue);
 
        public static Color DimGray => new Color(KnownColor.DimGray);
 
        public static Color DodgerBlue => new Color(KnownColor.DodgerBlue);
 
        public static Color Firebrick => new Color(KnownColor.Firebrick);
 
        public static Color FloralWhite => new Color(KnownColor.FloralWhite);
 
        public static Color ForestGreen => new Color(KnownColor.ForestGreen);
 
        public static Color Fuchsia => new Color(KnownColor.Fuchsia);
 
        public static Color Gainsboro => new Color(KnownColor.Gainsboro);
 
        public static Color GhostWhite => new Color(KnownColor.GhostWhite);
 
        public static Color Gold => new Color(KnownColor.Gold);
 
        public static Color Goldenrod => new Color(KnownColor.Goldenrod);
 
        public static Color Gray => new Color(KnownColor.Gray);
 
        public static Color Green => new Color(KnownColor.Green);
 
        public static Color GreenYellow => new Color(KnownColor.GreenYellow);
 
        public static Color Honeydew => new Color(KnownColor.Honeydew);
 
        public static Color HotPink => new Color(KnownColor.HotPink);
 
        public static Color IndianRed => new Color(KnownColor.IndianRed);
 
        public static Color Indigo => new Color(KnownColor.Indigo);
 
        public static Color Ivory => new Color(KnownColor.Ivory);
 
        public static Color Khaki => new Color(KnownColor.Khaki);
 
        public static Color Lavender => new Color(KnownColor.Lavender);
 
        public static Color LavenderBlush => new Color(KnownColor.LavenderBlush);
 
        public static Color LawnGreen => new Color(KnownColor.LawnGreen);
 
        public static Color LemonChiffon => new Color(KnownColor.LemonChiffon);
 
        public static Color LightBlue => new Color(KnownColor.LightBlue);
 
        public static Color LightCoral => new Color(KnownColor.LightCoral);
 
        public static Color LightCyan => new Color(KnownColor.LightCyan);
 
        public static Color LightGoldenrodYellow => new Color(KnownColor.LightGoldenrodYellow);
 
        public static Color LightGreen => new Color(KnownColor.LightGreen);
 
        public static Color LightGray => new Color(KnownColor.LightGray);
 
        public static Color LightPink => new Color(KnownColor.LightPink);
 
        public static Color LightSalmon => new Color(KnownColor.LightSalmon);
 
        public static Color LightSeaGreen => new Color(KnownColor.LightSeaGreen);
 
        public static Color LightSkyBlue => new Color(KnownColor.LightSkyBlue);
 
        public static Color LightSlateGray => new Color(KnownColor.LightSlateGray);
 
        public static Color LightSteelBlue => new Color(KnownColor.LightSteelBlue);
 
        public static Color LightYellow => new Color(KnownColor.LightYellow);
 
        public static Color Lime => new Color(KnownColor.Lime);
 
        public static Color LimeGreen => new Color(KnownColor.LimeGreen);
 
        public static Color Linen => new Color(KnownColor.Linen);
 
        public static Color Magenta => new Color(KnownColor.Magenta);
 
        public static Color Maroon => new Color(KnownColor.Maroon);
 
        public static Color MediumAquamarine => new Color(KnownColor.MediumAquamarine);
 
        public static Color MediumBlue => new Color(KnownColor.MediumBlue);
 
        public static Color MediumOrchid => new Color(KnownColor.MediumOrchid);
 
        public static Color MediumPurple => new Color(KnownColor.MediumPurple);
 
        public static Color MediumSeaGreen => new Color(KnownColor.MediumSeaGreen);
 
        public static Color MediumSlateBlue => new Color(KnownColor.MediumSlateBlue);
 
        public static Color MediumSpringGreen => new Color(KnownColor.MediumSpringGreen);
 
        public static Color MediumTurquoise => new Color(KnownColor.MediumTurquoise);
 
        public static Color MediumVioletRed => new Color(KnownColor.MediumVioletRed);
 
        public static Color MidnightBlue => new Color(KnownColor.MidnightBlue);
 
        public static Color MintCream => new Color(KnownColor.MintCream);
 
        public static Color MistyRose => new Color(KnownColor.MistyRose);
 
        public static Color Moccasin => new Color(KnownColor.Moccasin);
 
        public static Color NavajoWhite => new Color(KnownColor.NavajoWhite);
 
        public static Color Navy => new Color(KnownColor.Navy);
 
        public static Color OldLace => new Color(KnownColor.OldLace);
 
        public static Color Olive => new Color(KnownColor.Olive);
 
        public static Color OliveDrab => new Color(KnownColor.OliveDrab);
 
        public static Color Orange => new Color(KnownColor.Orange);
 
        public static Color OrangeRed => new Color(KnownColor.OrangeRed);
 
        public static Color Orchid => new Color(KnownColor.Orchid);
 
        public static Color PaleGoldenrod => new Color(KnownColor.PaleGoldenrod);
 
        public static Color PaleGreen => new Color(KnownColor.PaleGreen);
 
        public static Color PaleTurquoise => new Color(KnownColor.PaleTurquoise);
 
        public static Color PaleVioletRed => new Color(KnownColor.PaleVioletRed);
 
        public static Color PapayaWhip => new Color(KnownColor.PapayaWhip);
 
        public static Color PeachPuff => new Color(KnownColor.PeachPuff);
 
        public static Color Peru => new Color(KnownColor.Peru);
 
        public static Color Pink => new Color(KnownColor.Pink);
 
        public static Color Plum => new Color(KnownColor.Plum);
 
        public static Color PowderBlue => new Color(KnownColor.PowderBlue);
 
        public static Color Purple => new Color(KnownColor.Purple);
 
        /// <summary>
        /// Gets a system-defined color that has an ARGB value of <c>#663399</c>.
        /// </summary>
        /// <value>A system-defined color.</value>
        public static Color RebeccaPurple => new Color(KnownColor.RebeccaPurple);
 
        public static Color Red => new Color(KnownColor.Red);
 
        public static Color RosyBrown => new Color(KnownColor.RosyBrown);
 
        public static Color RoyalBlue => new Color(KnownColor.RoyalBlue);
 
        public static Color SaddleBrown => new Color(KnownColor.SaddleBrown);
 
        public static Color Salmon => new Color(KnownColor.Salmon);
 
        public static Color SandyBrown => new Color(KnownColor.SandyBrown);
 
        public static Color SeaGreen => new Color(KnownColor.SeaGreen);
 
        public static Color SeaShell => new Color(KnownColor.SeaShell);
 
        public static Color Sienna => new Color(KnownColor.Sienna);
 
        public static Color Silver => new Color(KnownColor.Silver);
 
        public static Color SkyBlue => new Color(KnownColor.SkyBlue);
 
        public static Color SlateBlue => new Color(KnownColor.SlateBlue);
 
        public static Color SlateGray => new Color(KnownColor.SlateGray);
 
        public static Color Snow => new Color(KnownColor.Snow);
 
        public static Color SpringGreen => new Color(KnownColor.SpringGreen);
 
        public static Color SteelBlue => new Color(KnownColor.SteelBlue);
 
        public static Color Tan => new Color(KnownColor.Tan);
 
        public static Color Teal => new Color(KnownColor.Teal);
 
        public static Color Thistle => new Color(KnownColor.Thistle);
 
        public static Color Tomato => new Color(KnownColor.Tomato);
 
        public static Color Turquoise => new Color(KnownColor.Turquoise);
 
        public static Color Violet => new Color(KnownColor.Violet);
 
        public static Color Wheat => new Color(KnownColor.Wheat);
 
        public static Color White => new Color(KnownColor.White);
 
        public static Color WhiteSmoke => new Color(KnownColor.WhiteSmoke);
 
        public static Color Yellow => new Color(KnownColor.Yellow);
 
        public static Color YellowGreen => new Color(KnownColor.YellowGreen);
        //
        //  end "web" colors
        // -------------------------------------------------------------------
 
        // NOTE : The "zero" pattern (all members being 0) must represent
        //      : "not set". This allows "Color c;" to be correct.
 
        private const short StateKnownColorValid = 0x0001;
        private const short StateARGBValueValid = 0x0002;
        private const short StateValueMask = StateARGBValueValid;
        private const short StateNameValid = 0x0008;
        private const long NotDefinedValue = 0;
 
        // Shift counts and bit masks for A, R, G, B components in ARGB mode
 
        internal const int ARGBAlphaShift = 24;
        internal const int ARGBRedShift = 16;
        internal const int ARGBGreenShift = 8;
        internal const int ARGBBlueShift = 0;
        internal const uint ARGBAlphaMask = 0xFFu << ARGBAlphaShift;
        internal const uint ARGBRedMask = 0xFFu << ARGBRedShift;
        internal const uint ARGBGreenMask = 0xFFu << ARGBGreenShift;
        internal const uint ARGBBlueMask = 0xFFu << ARGBBlueShift;
 
        // User supplied name of color. Will not be filled in if
        // we map to a "knowncolor"
        private readonly string? name; // Do not rename (binary serialization)
 
        // Standard 32bit sRGB (ARGB)
        private readonly long value; // Do not rename (binary serialization)
 
        // Ignored, unless "state" says it is valid
        private readonly short knownColor; // Do not rename (binary serialization)
 
        // State flags.
        private readonly short state; // Do not rename (binary serialization)
 
        internal Color(KnownColor knownColor)
        {
            value = 0;
            state = StateKnownColorValid;
            name = null;
            this.knownColor = unchecked((short)knownColor);
        }
 
        private Color(long value, short state, string? name, KnownColor knownColor)
        {
            this.value = value;
            this.state = state;
            this.name = name;
            this.knownColor = unchecked((short)knownColor);
        }
 
        public byte R => unchecked((byte)(Value >> ARGBRedShift));
 
        public byte G => unchecked((byte)(Value >> ARGBGreenShift));
 
        public byte B => unchecked((byte)(Value >> ARGBBlueShift));
 
        public byte A => unchecked((byte)(Value >> ARGBAlphaShift));
 
        public bool IsKnownColor => (state & StateKnownColorValid) != 0;
 
        public bool IsEmpty => state == 0;
 
        public bool IsNamedColor => ((state & StateNameValid) != 0) || IsKnownColor;
 
        public bool IsSystemColor => IsKnownColor && IsKnownColorSystem((KnownColor)knownColor);
 
        internal static bool IsKnownColorSystem(KnownColor knownColor)
            => KnownColorTable.ColorKindTable[(int)knownColor] == KnownColorTable.KnownColorKindSystem;
 
        // Used for the [DebuggerDisplay]. Inlining in the attribute is possible, but
        // against best practices as the current project language parses the string with
        // language specific heuristics.
 
        private string NameAndARGBValue => $"{{Name = {Name}, ARGB = ({A}, {R}, {G}, {B})}}";
 
        public string Name
        {
            get
            {
                if ((state & StateNameValid) != 0)
                {
                    Debug.Assert(name != null);
                    return name;
                }
 
                if (IsKnownColor)
                {
                    string tablename = KnownColorNames.KnownColorToName((KnownColor)knownColor);
                    Debug.Assert(tablename != null, $"Could not find known color '{(KnownColor)knownColor}' in the KnownColorTable");
 
                    return tablename;
                }
 
                // if we reached here, just encode the value
                //
                return value.ToString("x");
            }
        }
 
        private long Value
        {
            get
            {
                if ((state & StateValueMask) != 0)
                {
                    return value;
                }
 
                // This is the only place we have system colors value exposed
                if (IsKnownColor)
                {
                    return KnownColorTable.KnownColorToArgb((KnownColor)knownColor);
                }
 
                return NotDefinedValue;
            }
        }
 
        private static void CheckByte(int value, string name)
        {
            static void ThrowOutOfByteRange(int v, string n) =>
                throw new ArgumentException(SR.Format(SR.InvalidEx2BoundArgument, n, v, byte.MinValue, byte.MaxValue));
 
            if (unchecked((uint)value) > byte.MaxValue)
                ThrowOutOfByteRange(value, name);
        }
 
        private static Color FromArgb(uint argb) => new Color(argb, StateARGBValueValid, null, (KnownColor)0);
 
        public static Color FromArgb(int argb) => FromArgb(unchecked((uint)argb));
 
        public static Color FromArgb(int alpha, int red, int green, int blue)
        {
            CheckByte(alpha, nameof(alpha));
            CheckByte(red, nameof(red));
            CheckByte(green, nameof(green));
            CheckByte(blue, nameof(blue));
 
            return FromArgb(
                (uint)alpha << ARGBAlphaShift |
                (uint)red << ARGBRedShift |
                (uint)green << ARGBGreenShift |
                (uint)blue << ARGBBlueShift
            );
        }
 
        public static Color FromArgb(int alpha, Color baseColor)
        {
            CheckByte(alpha, nameof(alpha));
 
            return FromArgb(
                (uint)alpha << ARGBAlphaShift |
                (uint)baseColor.Value & ~ARGBAlphaMask
            );
        }
 
        public static Color FromArgb(int red, int green, int blue) => FromArgb(byte.MaxValue, red, green, blue);
 
        public static Color FromKnownColor(KnownColor color) =>
            color <= 0 || color > KnownColor.RebeccaPurple ? FromName(color.ToString()) : new Color(color);
 
        public static Color FromName(string name)
        {
            // try to get a known color first
            if (ColorTable.TryGetNamedColor(name, out Color color))
                return color;
 
            // otherwise treat it as a named color
            return new Color(NotDefinedValue, StateNameValid, name, (KnownColor)0);
        }
 
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        internal void GetRgbValues(out int r, out int g, out int b)
        {
            uint value = (uint)Value;
            r = (int)(value & ARGBRedMask) >> ARGBRedShift;
            g = (int)(value & ARGBGreenMask) >> ARGBGreenShift;
            b = (int)(value & ARGBBlueMask) >> ARGBBlueShift;
        }
 
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static void MinMaxRgb(out int min, out int max, int r, int g, int b)
        {
            if (r > g)
            {
                max = r;
                min = g;
            }
            else
            {
                max = g;
                min = r;
            }
            if (b > max)
            {
                max = b;
            }
            else if (b < min)
            {
                min = b;
            }
        }
 
        public float GetBrightness()
        {
            GetRgbValues(out int r, out int g, out int b);
 
            MinMaxRgb(out int min, out int max, r, g, b);
 
            return (max + min) / (byte.MaxValue * 2f);
        }
 
        public float GetHue()
        {
            GetRgbValues(out int r, out int g, out int b);
 
            if (r == g && g == b)
                return 0f;
 
            MinMaxRgb(out int min, out int max, r, g, b);
 
            float delta = max - min;
            float hue;
 
            if (r == max)
                hue = (g - b) / delta;
            else if (g == max)
                hue = (b - r) / delta + 2f;
            else
                hue = (r - g) / delta + 4f;
 
            hue *= 60f;
            if (hue < 0f)
                hue += 360f;
 
            return hue;
        }
 
        public float GetSaturation()
        {
            GetRgbValues(out int r, out int g, out int b);
 
            if (r == g && g == b)
                return 0f;
 
            MinMaxRgb(out int min, out int max, r, g, b);
 
            int div = max + min;
            if (div > byte.MaxValue)
                div = byte.MaxValue * 2 - max - min;
 
            return (max - min) / (float)div;
        }
 
        public int ToArgb() => unchecked((int)Value);
 
        public KnownColor ToKnownColor() => (KnownColor)knownColor;
 
        public override string ToString() =>
            IsNamedColor ? $"{nameof(Color)} [{Name}]" :
            (state & StateValueMask) != 0 ? $"{nameof(Color)} [A={A}, R={R}, G={G}, B={B}]" :
            $"{nameof(Color)} [Empty]";
 
        public static bool operator ==(Color left, Color right) =>
            left.value == right.value
                && left.state == right.state
                && left.knownColor == right.knownColor
                && left.name == right.name;
 
        public static bool operator !=(Color left, Color right) => !(left == right);
 
        public override bool Equals([NotNullWhen(true)] object? obj) => obj is Color other && Equals(other);
 
        public bool Equals(Color other) => this == other;
 
        public override int GetHashCode()
        {
            // Three cases:
            // 1. We don't have a name. All relevant data, including this fact, is in the remaining fields.
            // 2. We have a known name. The name will be the same instance of any other with the same
            // knownColor value, so we can ignore it for hashing. Note this also hashes different to
            // an unnamed color with the same ARGB value.
            // 3. Have an unknown name. Will differ from other unknown-named colors only by name, so we
            // can usefully use the names hash code alone.
            if (name != null && !IsKnownColor)
                return name.GetHashCode();
 
            return HashCode.Combine(value.GetHashCode(), state.GetHashCode(), knownColor.GetHashCode());
        }
    }
}