6

how can I generate a "random constant colour" for a given string at runtime?

So a given string value will always have the same colour but different strings will have different colours.

Like how gmail assigns colours to the sender names.

Thanks

Responses to comments:

  1. Thinking to generate the colour from a hashcode.
  2. The colours won't be stored but generated from a hashcode.
CodingHero
  • 2,865
  • 6
  • 29
  • 42
  • How color is stored? – pwas Oct 07 '15 at 14:04
  • is there a constraint to the strings? is there a minimal length, max length? – maraaaaaaaa Oct 07 '15 at 14:05
  • You can easily create random colours from almost anything via RGB values. Storing this information is not difficult either. What have you tried? – varocarbas Oct 07 '15 at 14:07
  • Depending on the length you could change the string divide that into three (or four) blocks. Get the character value from said blocks and use these for RGB (or RGBA) – Blaatz0r Oct 07 '15 at 14:08

4 Answers4

10

I don't know any dedicated method for this, but here is a simple method generating Hexadecimal values with MD5 based on a given string:

using System.Security.Cryptography;
using System.Text;


static string GetColor(string raw)
{
    using (MD5 md5Hash = MD5.Create())
    {
        byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(raw));
        return BitConverter.ToString(data).Replace("-", string.Empty).Substring(0, 6);
    }
}

Examples:

  1. example@example.com

    -> 23463B

  2. info@google.com

    -> 3C9015

  3. stack@exchange.com

    -> 7CA5E8

Edit:

I didn't tested it enough, so you may want to tweak it a little bit to get more different and unique values.

Edit2:

If you want transparency, check out this question/answer. By setting the Substring to Substring(0,8) , you should return a string with the alpha channel.

Community
  • 1
  • 1
Alex H
  • 1,424
  • 1
  • 11
  • 25
1

Similar to what the other answers are suggesting (hash the string in some form then use that hash to pick the color), but instead of using the hash to directly calculate the color use it as the index to an array of "Acceptable" colors.

class ColorPicker
{
    public ColorPicker(int colorCount)
    {
        //The ".Skip(2)" makes it skip pure white and pure black.
        // If you want those two, take out the +2 and the skip.
        _colors = ColorGenerator.Generate(colorCount + 2).Skip(2).ToArray();
    }
    private readonly Color[] _colors;

    public Color StringToColor(string message)
    {
        int someHash = CalculateHashOfStringSomehow(message);
        return _colors[someHash % _colors.Length];
    }

    private int CalculateHashOfStringSomehow(string message)
    {
        //TODO: I would not use "message.GetHashCode()" as you are not
        // guaranteed the same value between runs of the program.
        // Make up your own algorithom or use a existing one that has a fixed 
        // output for a given input, like MD5.
    }
}

This prevents issues like getting a white color when you plan on showing the text with a white background and other similar problems.

To populate your Color[] see this answer for the ColorGenerator class or just make your own pre-defined list of colors that look good on whatever background they will be used on.


Appendix:
In case the link goes down, here is a copy of the ColorGenerator class

public static class ColorGenerator
{

    // RYB color space
    private static class RYB
    {
        private static readonly double[] White = { 1, 1, 1 };
        private static readonly double[] Red = { 1, 0, 0 };
        private static readonly double[] Yellow = { 1, 1, 0 };
        private static readonly double[] Blue = { 0.163, 0.373, 0.6 };
        private static readonly double[] Violet = { 0.5, 0, 0.5 };
        private static readonly double[] Green = { 0, 0.66, 0.2 };
        private static readonly double[] Orange = { 1, 0.5, 0 };
        private static readonly double[] Black = { 0.2, 0.094, 0.0 };

        public static double[] ToRgb(double r, double y, double b)
        {
            var rgb = new double[3];
            for (int i = 0; i < 3; i++)
            {
                rgb[i] = White[i]  * (1.0 - r) * (1.0 - b) * (1.0 - y) +
                         Red[i]    * r         * (1.0 - b) * (1.0 - y) +
                         Blue[i]   * (1.0 - r) * b         * (1.0 - y) +
                         Violet[i] * r         * b         * (1.0 - y) +
                         Yellow[i] * (1.0 - r) * (1.0 - b) *        y +
                         Orange[i] * r         * (1.0 - b) *        y +
                         Green[i]  * (1.0 - r) * b         *        y +
                         Black[i]  * r         * b         *        y;
            }

            return rgb;
        }
    }

    private class Points : IEnumerable<double[]>
    {
        private readonly int pointsCount;
        private double[] picked;
        private int pickedCount;

        private readonly List<double[]> points = new List<double[]>();

        public Points(int count)
        {
            pointsCount = count;
        }

        private void Generate()
        {
            points.Clear();
            var numBase = (int)Math.Ceiling(Math.Pow(pointsCount, 1.0 / 3.0));
            var ceil = (int)Math.Pow(numBase, 3.0);
            for (int i = 0; i < ceil; i++)
            {
                points.Add(new[]
                {
                    Math.Floor(i/(double)(numBase*numBase))/ (numBase - 1.0),
                    Math.Floor((i/(double)numBase) % numBase)/ (numBase - 1.0),
                    Math.Floor((double)(i % numBase))/ (numBase - 1.0),
                });
            }
        }

        private double Distance(double[] p1)
        {
            double distance = 0;
            for (int i = 0; i < 3; i++)
            {
                distance += Math.Pow(p1[i] - picked[i], 2.0);
            }

            return distance;
        }

        private double[] Pick()
        {
            if (picked == null)
            {
                picked = points[0];
                points.RemoveAt(0);
                pickedCount = 1;
                return picked;
            }

            var d1 = Distance(points[0]);
            int i1 = 0, i2 = 0;
            foreach (var point in points)
            {
                var d2 = Distance(point);
                if (d1 < d2)
                {
                    i1 = i2;
                    d1 = d2;
                }

                i2 += 1;
            }

            var pick = points[i1];
            points.RemoveAt(i1);

            for (int i = 0; i < 3; i++)
            {
                picked[i] = (pickedCount * picked[i] + pick[i]) / (pickedCount + 1.0);
            }

            pickedCount += 1;
            return pick;
        }

        public IEnumerator<double[]> GetEnumerator()
        {
            Generate();
            for (int i = 0; i < pointsCount; i++)
            {
                yield return Pick();
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }

    public static IEnumerable<Color> Generate(int numOfColors)
    {
        var points = new Points(numOfColors);

        foreach (var point in points)
        {
            var rgb = RYB.ToRgb(point[0], point[1], point[2]);
            yield return Color.FromArgb(
                (int)Math.Floor(255 * rgb[0]),
                (int)Math.Floor(255 * rgb[1]),
                (int)Math.Floor(255 * rgb[2]));
        }
    }
}
Community
  • 1
  • 1
Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431
0

3 integer variables, r,g and b.

Loop through each character in the string in steps of 3 and add the character code.

r += n + 0
g += n + 1
b += n + 2

after the loop take r,g, and b modulo 255 and create a color using Color.FromARGB.

No guarantees the color will be pretty though, and some strings may happen to have colors very close to each other.

Bradley Uffner
  • 16,641
  • 3
  • 39
  • 76
0

I see some pretty good answeers but though it whould contribute with a little fun solutuion to generate colors from string, the Hash version looks like the best way to go but if this gives any one some inspiration to bould off, have at it

        ConsoleKeyInfo ch = new ConsoleKeyInfo();
        while (ch.KeyChar != 'e')
        {
            Console.WriteLine("type string to seed color");
            string s = Console.ReadLine(); // gets text from input, in this case the command line
            double d=0;
            foreach(char cha in s.ToCharArray())
            {
                d=+ (int)cha; // get the value and adds it 
            }
            d= (255/(Math.Pow(0.2,-0.002 *d))); // Generates a seed like value from i where 255 is the maximum. basicly 255/0.2^(-0.002*d)

            int i = Convert.ToInt32(d); //then convets and get rid of the decimels
            Color c = Color.FromArgb(i, i, i);// add a bit more calculation for varieng colers.
            Console.WriteLine(c.Name);
            Console.WriteLine("To Exit press e");
            ch = Console.ReadKey()
       }

Edit1: It definantly needs some tweeking, since the longer the string the ligther the color, but i think something can come from it with a little work :)