File: System\Drawing\ColorTranslator.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.Collections;
using System.Collections.Generic;
using System.Globalization;
 
namespace System.Drawing
{
    /// <summary>
    /// Translates colors to and from GDI+ <see cref='Color'/> objects.
    /// </summary>
    public static class ColorTranslator
    {
        // COLORREF is 0x00BBGGRR
        internal const int COLORREF_RedShift = 0;
        internal const int COLORREF_GreenShift = 8;
        internal const int COLORREF_BlueShift = 16;
 
        private const int OleSystemColorFlag = unchecked((int)0x80000000);
 
        private static Dictionary<string, Color>? s_htmlSysColorTable;
 
        internal static uint COLORREFToARGB(uint value)
            => ((value >> COLORREF_RedShift) & 0xFF) << Color.ARGBRedShift
                | ((value >> COLORREF_GreenShift) & 0xFF) << Color.ARGBGreenShift
                | ((value >> COLORREF_BlueShift) & 0xFF) << Color.ARGBBlueShift
                | Color.ARGBAlphaMask; // COLORREF's are always fully opaque
 
        /// <summary>
        /// Translates the specified <see cref='Color'/> to a Win32 color.
        /// </summary>
        public static int ToWin32(Color c)
        {
            // KnownColor Color values causes a table lookup or OS call for
            // every access, as such we manually extract the RGB values.
 
            c.GetRgbValues(out int r, out int g, out int b);
            return r << COLORREF_RedShift | g << COLORREF_GreenShift | b << COLORREF_BlueShift;
        }
 
        /// <summary>
        /// Translates the specified <see cref='Color'/> to an Ole color.
        /// </summary>
        public static int ToOle(Color c)
        {
            // IMPORTANT: This signature is invoked directly by the runtime marshaler and cannot change without
            // also updating the runtime.
 
            // This method converts Color to an OLE_COLOR.
            // https://learn.microsoft.com/openspecs/office_file_formats/ms-oforms/4b8f4be0-3fff-4e42-9fc1-b9fd00251e8e
 
            if (c.IsKnownColor && c.IsSystemColor)
            {
                // Unfortunately KnownColor didn't keep the same ordering as the various GetSysColor()
                // COLOR_ * values, otherwise this could be greatly simplified.
 
                switch (c.ToKnownColor())
                {
                    case KnownColor.ActiveBorder:
                        return unchecked((int)0x8000000A);
                    case KnownColor.ActiveCaption:
                        return unchecked((int)0x80000002);
                    case KnownColor.ActiveCaptionText:
                        return unchecked((int)0x80000009);
                    case KnownColor.AppWorkspace:
                        return unchecked((int)0x8000000C);
                    case KnownColor.ButtonFace:
                        return unchecked((int)0x8000000F);
                    case KnownColor.ButtonHighlight:
                        return unchecked((int)0x80000014);
                    case KnownColor.ButtonShadow:
                        return unchecked((int)0x80000010);
                    case KnownColor.Control:
                        return unchecked((int)0x8000000F);
                    case KnownColor.ControlDark:
                        return unchecked((int)0x80000010);
                    case KnownColor.ControlDarkDark:
                        return unchecked((int)0x80000015);
                    case KnownColor.ControlLight:
                        return unchecked((int)0x80000016);
                    case KnownColor.ControlLightLight:
                        return unchecked((int)0x80000014);
                    case KnownColor.ControlText:
                        return unchecked((int)0x80000012);
                    case KnownColor.Desktop:
                        return unchecked((int)0x80000001);
                    case KnownColor.GradientActiveCaption:
                        return unchecked((int)0x8000001B);
                    case KnownColor.GradientInactiveCaption:
                        return unchecked((int)0x8000001C);
                    case KnownColor.GrayText:
                        return unchecked((int)0x80000011);
                    case KnownColor.Highlight:
                        return unchecked((int)0x8000000D);
                    case KnownColor.HighlightText:
                        return unchecked((int)0x8000000E);
                    case KnownColor.HotTrack:
                        return unchecked((int)0x8000001A);
                    case KnownColor.InactiveBorder:
                        return unchecked((int)0x8000000B);
                    case KnownColor.InactiveCaption:
                        return unchecked((int)0x80000003);
                    case KnownColor.InactiveCaptionText:
                        return unchecked((int)0x80000013);
                    case KnownColor.Info:
                        return unchecked((int)0x80000018);
                    case KnownColor.InfoText:
                        return unchecked((int)0x80000017);
                    case KnownColor.Menu:
                        return unchecked((int)0x80000004);
                    case KnownColor.MenuBar:
                        return unchecked((int)0x8000001E);
                    case KnownColor.MenuHighlight:
                        return unchecked((int)0x8000001D);
                    case KnownColor.MenuText:
                        return unchecked((int)0x80000007);
                    case KnownColor.ScrollBar:
                        return unchecked((int)0x80000000);
                    case KnownColor.Window:
                        return unchecked((int)0x80000005);
                    case KnownColor.WindowFrame:
                        return unchecked((int)0x80000006);
                    case KnownColor.WindowText:
                        return unchecked((int)0x80000008);
                }
            }
 
            return ToWin32(c);
        }
 
        /// <summary>
        /// Translates an Ole color value to a GDI+ <see cref='Color'/>.
        /// </summary>
        public static Color FromOle(int oleColor)
        {
            // IMPORTANT: This signature is invoked directly by the runtime marshaler and cannot change without
            // also updating the runtime.
 
            if ((oleColor & OleSystemColorFlag) != 0)
            {
                switch (oleColor)
                {
                    case unchecked((int)0x8000000A):
                        return Color.FromKnownColor(KnownColor.ActiveBorder);
                    case unchecked((int)0x80000002):
                        return Color.FromKnownColor(KnownColor.ActiveCaption);
                    case unchecked((int)0x80000009):
                        return Color.FromKnownColor(KnownColor.ActiveCaptionText);
                    case unchecked((int)0x8000000C):
                        return Color.FromKnownColor(KnownColor.AppWorkspace);
                    case unchecked((int)0x8000000F):
                        return Color.FromKnownColor(KnownColor.Control);
                    case unchecked((int)0x80000010):
                        return Color.FromKnownColor(KnownColor.ControlDark);
                    case unchecked((int)0x80000015):
                        return Color.FromKnownColor(KnownColor.ControlDarkDark);
                    case unchecked((int)0x80000016):
                        return Color.FromKnownColor(KnownColor.ControlLight);
                    case unchecked((int)0x80000014):
                        return Color.FromKnownColor(KnownColor.ControlLightLight);
                    case unchecked((int)0x80000012):
                        return Color.FromKnownColor(KnownColor.ControlText);
                    case unchecked((int)0x80000001):
                        return Color.FromKnownColor(KnownColor.Desktop);
                    case unchecked((int)0x8000001B):
                        return Color.FromKnownColor(KnownColor.GradientActiveCaption);
                    case unchecked((int)0x8000001C):
                        return Color.FromKnownColor(KnownColor.GradientInactiveCaption);
                    case unchecked((int)0x80000011):
                        return Color.FromKnownColor(KnownColor.GrayText);
                    case unchecked((int)0x8000000D):
                        return Color.FromKnownColor(KnownColor.Highlight);
                    case unchecked((int)0x8000000E):
                        return Color.FromKnownColor(KnownColor.HighlightText);
                    case unchecked((int)0x8000001A):
                        return Color.FromKnownColor(KnownColor.HotTrack);
                    case unchecked((int)0x8000000B):
                        return Color.FromKnownColor(KnownColor.InactiveBorder);
                    case unchecked((int)0x80000003):
                        return Color.FromKnownColor(KnownColor.InactiveCaption);
                    case unchecked((int)0x80000013):
                        return Color.FromKnownColor(KnownColor.InactiveCaptionText);
                    case unchecked((int)0x80000018):
                        return Color.FromKnownColor(KnownColor.Info);
                    case unchecked((int)0x80000017):
                        return Color.FromKnownColor(KnownColor.InfoText);
                    case unchecked((int)0x80000004):
                        return Color.FromKnownColor(KnownColor.Menu);
                    case unchecked((int)0x8000001E):
                        return Color.FromKnownColor(KnownColor.MenuBar);
                    case unchecked((int)0x8000001D):
                        return Color.FromKnownColor(KnownColor.MenuHighlight);
                    case unchecked((int)0x80000007):
                        return Color.FromKnownColor(KnownColor.MenuText);
                    case unchecked((int)0x80000000):
                        return Color.FromKnownColor(KnownColor.ScrollBar);
                    case unchecked((int)0x80000005):
                        return Color.FromKnownColor(KnownColor.Window);
                    case unchecked((int)0x80000006):
                        return Color.FromKnownColor(KnownColor.WindowFrame);
                    case unchecked((int)0x80000008):
                        return Color.FromKnownColor(KnownColor.WindowText);
                }
            }
 
            // When we don't find a system color, we treat the color as a COLORREF
            return KnownColorTable.ArgbToKnownColor(COLORREFToARGB((uint)oleColor));
        }
 
        /// <summary>
        /// Translates an Win32 color value to a GDI+ <see cref='Color'/>.
        /// </summary>
        public static Color FromWin32(int win32Color)
        {
            return FromOle(win32Color);
        }
 
        /// <summary>
        /// Translates an Html color representation to a GDI+ <see cref='Color'/>.
        /// </summary>
        public static Color FromHtml(string htmlColor)
        {
            Color c = Color.Empty;
 
            // empty color
            if ((htmlColor == null) || (htmlColor.Length == 0))
                return c;
 
            // #RRGGBB or #RGB
            if ((htmlColor[0] == '#') &&
                ((htmlColor.Length == 7) || (htmlColor.Length == 4)))
            {
                if (htmlColor.Length == 7)
                {
                    c = Color.FromArgb(Convert.ToInt32(htmlColor.Substring(1, 2), 16),
                                       Convert.ToInt32(htmlColor.Substring(3, 2), 16),
                                       Convert.ToInt32(htmlColor.Substring(5, 2), 16));
                }
                else
                {
                    string r = char.ToString(htmlColor[1]);
                    string g = char.ToString(htmlColor[2]);
                    string b = char.ToString(htmlColor[3]);
 
                    c = Color.FromArgb(Convert.ToInt32(r + r, 16),
                                       Convert.ToInt32(g + g, 16),
                                       Convert.ToInt32(b + b, 16));
                }
            }
 
            // special case. Html requires LightGrey, but .NET uses LightGray
            if (c.IsEmpty && string.Equals(htmlColor, "LightGrey", StringComparison.OrdinalIgnoreCase))
            {
                c = Color.LightGray;
            }
 
            // System color
            if (c.IsEmpty)
            {
                if (s_htmlSysColorTable == null)
                {
                    InitializeHtmlSysColorTable();
                }
 
                s_htmlSysColorTable!.TryGetValue(htmlColor.ToLowerInvariant(), out c);
            }
 
            // resort to type converter which will handle named colors
            if (c.IsEmpty)
            {
                try
                {
                    c = ColorConverterCommon.ConvertFromString(htmlColor, CultureInfo.CurrentCulture);
                }
                catch (Exception ex)
                {
                    throw new ArgumentException(ex.Message, nameof(htmlColor), ex);
                }
            }
 
            return c;
        }
 
        /// <summary>
        /// Translates the specified <see cref='Color'/> to an Html string color representation.
        /// </summary>
        public static string ToHtml(Color c)
        {
            string colorString = string.Empty;
 
            if (c.IsEmpty)
                return colorString;
 
            if (c.IsSystemColor)
            {
                switch (c.ToKnownColor())
                {
                    case KnownColor.ActiveBorder:
                        colorString = "activeborder";
                        break;
                    case KnownColor.GradientActiveCaption:
                    case KnownColor.ActiveCaption:
                        colorString = "activecaption";
                        break;
                    case KnownColor.AppWorkspace:
                        colorString = "appworkspace";
                        break;
                    case KnownColor.Desktop:
                        colorString = "background";
                        break;
                    case KnownColor.Control:
                    case KnownColor.ControlLight:
                        colorString = "buttonface";
                        break;
                    case KnownColor.ControlDark:
                        colorString = "buttonshadow";
                        break;
                    case KnownColor.ControlText:
                        colorString = "buttontext";
                        break;
                    case KnownColor.ActiveCaptionText:
                        colorString = "captiontext";
                        break;
                    case KnownColor.GrayText:
                        colorString = "graytext";
                        break;
                    case KnownColor.HotTrack:
                    case KnownColor.Highlight:
                        colorString = "highlight";
                        break;
                    case KnownColor.MenuHighlight:
                    case KnownColor.HighlightText:
                        colorString = "highlighttext";
                        break;
                    case KnownColor.InactiveBorder:
                        colorString = "inactiveborder";
                        break;
                    case KnownColor.GradientInactiveCaption:
                    case KnownColor.InactiveCaption:
                        colorString = "inactivecaption";
                        break;
                    case KnownColor.InactiveCaptionText:
                        colorString = "inactivecaptiontext";
                        break;
                    case KnownColor.Info:
                        colorString = "infobackground";
                        break;
                    case KnownColor.InfoText:
                        colorString = "infotext";
                        break;
                    case KnownColor.MenuBar:
                    case KnownColor.Menu:
                        colorString = "menu";
                        break;
                    case KnownColor.MenuText:
                        colorString = "menutext";
                        break;
                    case KnownColor.ScrollBar:
                        colorString = "scrollbar";
                        break;
                    case KnownColor.ControlDarkDark:
                        colorString = "threeddarkshadow";
                        break;
                    case KnownColor.ControlLightLight:
                        colorString = "buttonhighlight";
                        break;
                    case KnownColor.Window:
                        colorString = "window";
                        break;
                    case KnownColor.WindowFrame:
                        colorString = "windowframe";
                        break;
                    case KnownColor.WindowText:
                        colorString = "windowtext";
                        break;
                }
            }
            else if (c.IsNamedColor)
            {
                if (c == Color.LightGray)
                {
                    // special case due to mismatch between Html and enum spelling
                    colorString = "LightGrey";
                }
                else
                {
                    colorString = c.Name;
                }
            }
            else
            {
                c.GetRgbValues(out int r, out int g, out int b);
                colorString = $"#{r:X2}{g:X2}{b:X2}";
            }
 
            return colorString;
        }
 
        private static void InitializeHtmlSysColorTable()
        {
            s_htmlSysColorTable = new Dictionary<string, Color>(27)
            {
                ["activeborder"] = Color.FromKnownColor(KnownColor.ActiveBorder),
                ["activecaption"] = Color.FromKnownColor(KnownColor.ActiveCaption),
                ["appworkspace"] = Color.FromKnownColor(KnownColor.AppWorkspace),
                ["background"] = Color.FromKnownColor(KnownColor.Desktop),
                ["buttonface"] = Color.FromKnownColor(KnownColor.Control),
                ["buttonhighlight"] = Color.FromKnownColor(KnownColor.ControlLightLight),
                ["buttonshadow"] = Color.FromKnownColor(KnownColor.ControlDark),
                ["buttontext"] = Color.FromKnownColor(KnownColor.ControlText),
                ["captiontext"] = Color.FromKnownColor(KnownColor.ActiveCaptionText),
                ["graytext"] = Color.FromKnownColor(KnownColor.GrayText),
                ["highlight"] = Color.FromKnownColor(KnownColor.Highlight),
                ["highlighttext"] = Color.FromKnownColor(KnownColor.HighlightText),
                ["inactiveborder"] = Color.FromKnownColor(KnownColor.InactiveBorder),
                ["inactivecaption"] = Color.FromKnownColor(KnownColor.InactiveCaption),
                ["inactivecaptiontext"] = Color.FromKnownColor(KnownColor.InactiveCaptionText),
                ["infobackground"] = Color.FromKnownColor(KnownColor.Info),
                ["infotext"] = Color.FromKnownColor(KnownColor.InfoText),
                ["menu"] = Color.FromKnownColor(KnownColor.Menu),
                ["menutext"] = Color.FromKnownColor(KnownColor.MenuText),
                ["scrollbar"] = Color.FromKnownColor(KnownColor.ScrollBar),
                ["threeddarkshadow"] = Color.FromKnownColor(KnownColor.ControlDarkDark),
                ["threedface"] = Color.FromKnownColor(KnownColor.Control),
                ["threedhighlight"] = Color.FromKnownColor(KnownColor.ControlLight),
                ["threedlightshadow"] = Color.FromKnownColor(KnownColor.ControlLightLight),
                ["window"] = Color.FromKnownColor(KnownColor.Window),
                ["windowframe"] = Color.FromKnownColor(KnownColor.WindowFrame),
                ["windowtext"] = Color.FromKnownColor(KnownColor.WindowText)
            };
        }
    }
}