开发者

Creating a C# Color from HSL values

How can I create a C# Color 开发者_JS百科from HSL values?


Add the following line below to the "using" namespaces in your code file:

using System.Runtime.InteropServices

and with P/Invoke, add the following function:

[DllImport("shlwapi.dll")]
public static extern int ColorHLSToRGB(int H, int L, int S);

In any class you want in your project.

If you have the color in hue, luminance and saturation as integers, then give these values to that function, and the desired RGB color will be returned as integer. Then use ColorTranslator.FromWin32 to translate the returned rgb color as integer to System.Drawing.Color structure.

Another way is to use Color.FromArgb(Int32) overload method, to create the color you want as System.Drawing.Color structure from that integer.

Another way is to create an instance of ColorConverter class, and then call ConvertFrom method, and give it the RGB Color integer returned from the ColorHLSToRGB function, or call ConvertTo method with the same first argument, but in the second argument input typeof(System.Drawing.Color). Then convert the returned object to System.Drawing.Color structure type.

The result is exactly what you are seeking and expected!

You also can try other algorithms or ways that you can search, find out and learn on the internet, about how to convert RGB color as integer to System.Drawing.Color structure OR how to convert HLS values as three integers to System.Drawing.Color structure!


I have a class that does RGB, HSB, and HSL transformations. I am NOT a color expert, and I also do not claim that this class has the fastest algorithms (it uses string.Convert in places to ensure floating point conversions come out as expected --- which might now be able to be revisited with some new BCL floating point conversion changes ...)

However, this class has been tested and working for quote a while, and seems to do the work correctly. I use it to create Frozen Wpf Brushes, which do not run in tight loops repeatedly (or in some performance critical rendering path); and the results seem to be as expected.

If you have critical concerns you should examine the code ... Here it is:

using System;
using System.Drawing;
using SystemMath = System.Math;


namespace Sc.Util.Rendering
{
    /// <summary>
    /// Static methods for transforming argb spaces and argb values.
    /// </summary>
    public static class SimpleColorTransforms
    {
        private static double tolerance
            => 0.000000000000001;


        /// <summary>
        /// Defines brightness levels.
        /// </summary>
        public enum Brightness
                : byte
        {
            Bright = 255,
            MediumBright = 210,
            Medium = 142,
            Dim = 98,
            XDim = 50
        }


        /// <summary>
        /// Defines alpha levels.
        /// </summary>
        public enum Alpha
                : byte
        {
            Opaque = 255,
            MediumHigh = 230,
            Medium = 175,
            MediumLow = 142,
            Low = 109,
            XLow = 45
        }


        /// <summary>
        /// Defines hint alpha levels.
        /// </summary>
        public enum HintAlpha
                : byte
        {
            Low = 64,
            XLow = 48,
            XxLow = 32,
            XxxLow = 16
        }


        /// <summary>
        /// Specifies a mode for argb transformations.
        /// </summary>
        public enum ColorTransformMode
                : byte
        {
            Hsl,
            Hsb
        }


        /// <summary>
        /// Converts RGB to HSL. Alpha is ignored.
        /// Output is: { H: [0, 360], S: [0, 1], L: [0, 1] }.
        /// </summary>
        /// <param name="color">The color to convert.</param>
        public static double[] RgBtoHsl(Color color)
        {
            double h = 0D;
            double s = 0D;
            double l;

            // normalize red, green, blue values
            double r = color.R / 255D;
            double g = color.G / 255D;
            double b = color.B / 255D;

            double max = SystemMath.Max(r, SystemMath.Max(g, b));
            double min = SystemMath.Min(r, SystemMath.Min(g, b));

            // hue
            if (SystemMath.Abs(max - min) < SimpleColorTransforms.tolerance)
                h = 0D; // undefined
            else if ((SystemMath.Abs(max - r) < SimpleColorTransforms.tolerance)
                    && (g >= b))
                h = (60D * (g - b)) / (max - min);
            else if ((SystemMath.Abs(max - r) < SimpleColorTransforms.tolerance)
                    && (g < b))
                h = ((60D * (g - b)) / (max - min)) + 360D;
            else if (SystemMath.Abs(max - g) < SimpleColorTransforms.tolerance)
                h = ((60D * (b - r)) / (max - min)) + 120D;
            else if (SystemMath.Abs(max - b) < SimpleColorTransforms.tolerance)
                h = ((60D * (r - g)) / (max - min)) + 240D;

            // luminance
            l = (max + min) / 2D;

            // saturation
            if ((SystemMath.Abs(l) < SimpleColorTransforms.tolerance)
                    || (SystemMath.Abs(max - min) < SimpleColorTransforms.tolerance))
                s = 0D;
            else if ((0D < l)
                    && (l <= .5D))
                s = (max - min) / (max + min);
            else if (l > .5D)
                s = (max - min) / (2D - (max + min)); //(max-min > 0)?

            return new[]
            {
                SystemMath.Max(0D, SystemMath.Min(360D, double.Parse($"{h:0.##}"))),
                SystemMath.Max(0D, SystemMath.Min(1D, double.Parse($"{s:0.##}"))),
                SystemMath.Max(0D, SystemMath.Min(1D, double.Parse($"{l:0.##}")))
            };
        }


        /// <summary>
        /// Converts HSL to RGB, with a specified output Alpha.
        /// Arguments are limited to the defined range:
        /// does not raise exceptions.
        /// </summary>
        /// <param name="h">Hue, must be in [0, 360].</param>
        /// <param name="s">Saturation, must be in [0, 1].</param>
        /// <param name="l">Luminance, must be in [0, 1].</param>
        /// <param name="a">Output Alpha, must be in [0, 255].</param>
        public static Color HsLtoRgb(double h, double s, double l, int a = 255)
        {
            h = SystemMath.Max(0D, SystemMath.Min(360D, h));
            s = SystemMath.Max(0D, SystemMath.Min(1D, s));
            l = SystemMath.Max(0D, SystemMath.Min(1D, l));
            a = SystemMath.Max(0, SystemMath.Min(255, a));

            // achromatic argb (gray scale)
            if (SystemMath.Abs(s) < SimpleColorTransforms.tolerance) {
                return Color.FromArgb(
                        a,
                        SystemMath.Max(0, SystemMath.Min(255, Convert.ToInt32(double.Parse($"{l * 255D:0.00}")))),
                        SystemMath.Max(0, SystemMath.Min(255, Convert.ToInt32(double.Parse($"{l * 255D:0.00}")))),
                        SystemMath.Max(0, SystemMath.Min(255, Convert.ToInt32(double.Parse($"{l * 255D:0.00}")))));
            }

            double q = l < .5D
                    ? l * (1D + s)
                    : (l + s) - (l * s);
            double p = (2D * l) - q;

            double hk = h / 360D;
            double[] T = new double[3];
            T[0] = hk + (1D / 3D); // Tr
            T[1] = hk; // Tb
            T[2] = hk - (1D / 3D); // Tg

            for (int i = 0; i < 3; i++) {
                if (T[i] < 0D)
                    T[i] += 1D;
                if (T[i] > 1D)
                    T[i] -= 1D;

                if ((T[i] * 6D) < 1D)
                    T[i] = p + ((q - p) * 6D * T[i]);
                else if ((T[i] * 2D) < 1)
                    T[i] = q;
                else if ((T[i] * 3D) < 2)
                    T[i] = p + ((q - p) * ((2D / 3D) - T[i]) * 6D);
                else
                    T[i] = p;
            }

            return Color.FromArgb(
                    a,
                    SystemMath.Max(0, SystemMath.Min(255, Convert.ToInt32(double.Parse($"{T[0] * 255D:0.00}")))),
                    SystemMath.Max(0, SystemMath.Min(255, Convert.ToInt32(double.Parse($"{T[1] * 255D:0.00}")))),
                    SystemMath.Max(0, SystemMath.Min(255, Convert.ToInt32(double.Parse($"{T[2] * 255D:0.00}")))));
        }


        /// <summary>
        /// Converts RGB to HSB. Alpha is ignored.
        /// Output is: { H: [0, 360], S: [0, 1], B: [0, 1] }.
        /// </summary>
        /// <param name="color">The color to convert.</param>
        public static double[] RgBtoHsb(Color color)
        {
            // normalize red, green and blue values
            double r = color.R / 255D;
            double g = color.G / 255D;
            double b = color.B / 255D;

            // conversion start
            double max = SystemMath.Max(r, SystemMath.Max(g, b));
            double min = SystemMath.Min(r, SystemMath.Min(g, b));

            double h = 0D;
            if ((SystemMath.Abs(max - r) < SimpleColorTransforms.tolerance)
                    && (g >= b))
                h = (60D * (g - b)) / (max - min);
            else if ((SystemMath.Abs(max - r) < SimpleColorTransforms.tolerance)
                    && (g < b))
                h = ((60D * (g - b)) / (max - min)) + 360D;
            else if (SystemMath.Abs(max - g) < SimpleColorTransforms.tolerance)
                h = ((60D * (b - r)) / (max - min)) + 120D;
            else if (SystemMath.Abs(max - b) < SimpleColorTransforms.tolerance)
                h = ((60D * (r - g)) / (max - min)) + 240D;

            double s = SystemMath.Abs(max) < SimpleColorTransforms.tolerance
                    ? 0D
                    : 1D - (min / max);

            return new[]
            {
                SystemMath.Max(0D, SystemMath.Min(360D, h)),
                SystemMath.Max(0D, SystemMath.Min(1D, s)),
                SystemMath.Max(0D, SystemMath.Min(1D, max))
            };
        }


        /// <summary>
        /// Converts HSB to RGB, with a specified output Alpha.
        /// Arguments are limited to the defined range:
        /// does not raise exceptions.
        /// </summary>
        /// <param name="h">Hue, must be in [0, 360].</param>
        /// <param name="s">Saturation, must be in [0, 1].</param>
        /// <param name="b">Brightness, must be in [0, 1].</param>
        /// <param name="a">Output Alpha, must be in [0, 255].</param>
        public static Color HsBtoRgb(double h, double s, double b, int a = 255)
        {
            h = SystemMath.Max(0D, SystemMath.Min(360D, h));
            s = SystemMath.Max(0D, SystemMath.Min(1D, s));
            b = SystemMath.Max(0D, SystemMath.Min(1D, b));
            a = SystemMath.Max(0, SystemMath.Min(255, a));

            double r = 0D;
            double g = 0D;
            double bl = 0D;

            if (SystemMath.Abs(s) < SimpleColorTransforms.tolerance)
                r = g = bl = b;
            else {
                // the argb wheel consists of 6 sectors. Figure out which sector
                // you're in.
                double sectorPos = h / 60D;
                int sectorNumber = (int)SystemMath.Floor(sectorPos);
                // get the fractional part of the sector
                double fractionalSector = sectorPos - sectorNumber;

                // calculate values for the three axes of the argb.
                double p = b * (1D - s);
                double q = b * (1D - (s * fractionalSector));
                double t = b * (1D - (s * (1D - fractionalSector)));

                // assign the fractional colors to r, g, and b based on the sector
                // the angle is in.
                switch (sectorNumber) {
                    case 0 :
                        r = b;
                        g = t;
                        bl = p;
                        break;
                    case 1 :
                        r = q;
                        g = b;
                        bl = p;
                        break;
                    case 2 :
                        r = p;
                        g = b;
                        bl = t;
                        break;
                    case 3 :
                        r = p;
                        g = q;
                        bl = b;
                        break;
                    case 4 :
                        r = t;
                        g = p;
                        bl = b;
                        break;
                    case 5 :
                        r = b;
                        g = p;
                        bl = q;
                        break;
                }
            }

            return Color.FromArgb(
                    a,
                    SystemMath.Max(0, SystemMath.Min(255, Convert.ToInt32(double.Parse($"{r * 255D:0.00}")))),
                    SystemMath.Max(0, SystemMath.Min(255, Convert.ToInt32(double.Parse($"{g * 255D:0.00}")))),
                    SystemMath.Max(0, SystemMath.Min(255, Convert.ToInt32(double.Parse($"{bl * 250D:0.00}")))));
        }


        /// <summary>
        /// Multiplies the Color's Luminance or Brightness by the argument;
        /// and optionally specifies the output Alpha.
        /// </summary>
        /// <param name="color">The color to transform.</param>
        /// <param name="colorTransformMode">Transform mode.</param>
        /// <param name="brightnessTransform">The transformation multiplier.</param>
        /// <param name="outputAlpha">Can optionally specify the Alpha to directly
        /// set on the output. If null, then the input <paramref name="color"/>
        /// Alpha is used.</param>
        public static Color TransformBrightness(
                Color color,
                ColorTransformMode colorTransformMode,
                double brightnessTransform,
                byte? outputAlpha = null)
        {
            double[] hsl = colorTransformMode == ColorTransformMode.Hsl
                    ? SimpleColorTransforms.RgBtoHsl(color)
                    : SimpleColorTransforms.RgBtoHsb(color);
            if ((SystemMath.Abs(hsl[2]) < SimpleColorTransforms.tolerance)
                    && (brightnessTransform > 1D))
                hsl[2] = brightnessTransform - 1D;
            else
                hsl[2] *= brightnessTransform;
            return colorTransformMode == ColorTransformMode.Hsl
                    ? SimpleColorTransforms.HsLtoRgb(hsl[0], hsl[1], hsl[2], outputAlpha ?? color.A)
                    : SimpleColorTransforms.HsBtoRgb(hsl[0], hsl[1], hsl[2], outputAlpha ?? color.A);
        }


        /// <summary>
        /// Multiplies the Color's Saturation, and Luminance or Brightness by the argument;
        /// and optionally specifies the output Alpha.
        /// </summary>
        /// <param name="color">The color to transform.</param>
        /// <param name="colorTransformMode">Transform mode.</param>
        /// <param name="saturationTransform">The transformation multiplier.</param>
        /// <param name="brightnessTransform">The transformation multiplier.</param>
        /// <param name="outputAlpha">Can optionally specify the Alpha to directly
        /// set on the output. If null, then the input <paramref name="color"/>
        /// Alpha is used.</param>
        public static Color TransformSaturationAndBrightness(
                Color color,
                ColorTransformMode colorTransformMode,
                double saturationTransform,
                double brightnessTransform,
                byte? outputAlpha = null)
        {
            double[] hsl = colorTransformMode == ColorTransformMode.Hsl
                    ? SimpleColorTransforms.RgBtoHsl(color)
                    : SimpleColorTransforms.RgBtoHsb(color);
            if ((SystemMath.Abs(hsl[1]) < SimpleColorTransforms.tolerance)
                    && (saturationTransform > 1D))
                hsl[1] = saturationTransform - 1D;
            else
                hsl[1] *= saturationTransform;
            if ((SystemMath.Abs(hsl[2]) < SimpleColorTransforms.tolerance)
                    && (brightnessTransform > 1D))
                hsl[2] = brightnessTransform - 1D;
            else
                hsl[2] *= brightnessTransform;
            return colorTransformMode == ColorTransformMode.Hsl
                    ? SimpleColorTransforms.HsLtoRgb(hsl[0], hsl[1], hsl[2], outputAlpha ?? color.A)
                    : SimpleColorTransforms.HsBtoRgb(hsl[0], hsl[1], hsl[2], outputAlpha ?? color.A);
        }


        /// <summary>
        /// Creates a new Color by combining R, G, and B from each Color, scaled by the Color's Alpha.
        /// The R, G, B of each Color is scaled by the Color's Alpha. The R, G, B of both results is
        /// then added together and divided by 2. The valuea are limited to [0, 255].
        /// The Alpha of the output Color is specified; and is also limited to [0, 255]
        /// (does not raise exceptions).
        /// </summary>
        /// <param name="color1">Combined by scaling RGB by the A.</param>
        /// <param name="color2">Combined by scaling RGB by the A.</param>
        /// <param name="outputAlpha">The Alpha of the output Color.</param>
        public static Color AlphaCombine(Color color1, Color color2, byte outputAlpha)
        {
            double a1 = color1.A / 255D;
            double a2 = color2.A / 255D;
            return Color.FromArgb(
                    outputAlpha,
                    (byte)SystemMath.Max(0D, SystemMath.Min(255D, ((color1.R * a1) + (color2.R * a2)) * .5D)),
                    (byte)SystemMath.Max(0D, SystemMath.Min(255D, ((color1.G * a1) + (color2.G * a2)) * .5D)),
                    (byte)SystemMath.Max(0D, SystemMath.Min(255D, ((color1.B * a1) + (color2.B * a2)) * .5D)));
        }
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜