2

Is there any way to simplify this line of code? Are two calls to Color.FromArgb() really necessary?

Color c = Color.FromArgb(255, Color.FromArgb(Convert.ToInt32(rand.Next(0x1000000))));

Without the duplicated Color.FromArgb() call I get only Alpha=0 colors.

References:

How do I generate random dark colors in C#?

abenci
  • 8,422
  • 19
  • 69
  • 134

4 Answers4

6

Just tried this in LinqPad and it seemed to do the trick:

var random = new Random();
System.Drawing.Color c;
unchecked
{
    var n = (int)0xFF000000 + (random.Next(0xFFFFFF) & 0x7F7F7F);
    Console.WriteLine($"ARGB: {n}");
    c = System.Drawing.Color.FromArgb(n);
}
Console.WriteLine($"A: {c.A}");
Console.WriteLine($"R: {c.R}");
Console.WriteLine($"G: {c.G}");
Console.WriteLine($"B: {c.B}");

More concisely, it would be:

var random = new Random();
Color c;
unchecked
{
    c = Color.FromArgb((int)0xFF000000 + (random.Next(0xFFFFFF) & 0x7F7F7F));
}

Or if you want to get really funky with bit manipulation (this is not more efficient, just saves you typing some 0s):

c = Color.FromArgb((int)(0xFF << 24 ^ (random.Next(0xFFFFFF) & 0x7F7F7F)));

Original poster pointed out that an extra pair of brackets eliminates the need for unchecked:

c = Color.FromArgb((int)(0xFF000000 + (random.Next(0xFFFFFF) & 0x7F7F7F)));

Bit of an explanation. ARGB is using a signed 32 bit int to represent four bytes, A, R, G, and B. We want the colour to be solid, so A needs to be 255 hence: 0xFF000000 Then random.Next(0xFFFFFF) generates a pseudo-random 24 bit number taking care of the R, G and B bytes. As the question only wanted dark colours we mask off the most significant bit of each byte. For a simple example, say the RNG spat out the max value (equivalent to white):

0xFFFFFF = 111111111111111111111111

We then do a bitwise AND to chop off the most significant bits:

0x7F7F7F = 011111110111111101111111

111111111111111111111111 & 011111110111111101111111 = 011111110111111101111111

Stevo
  • 1,424
  • 11
  • 20
  • Maybe `Convert.ToInt32()` helps to remove `unchecked` statement? – abenci Jun 20 '18 at 15:32
  • No, that causes an overflow exception. The problem is you need the top 8 bits to be set to 1 which means you end up with a negative number in signed int terms. – Stevo Jun 20 '18 at 15:39
  • 1
    It works with an additional couple of brackets: standard `Color.FromArgb((int) (0xFF000000 + rand.Next(0xFFFFFF)))` and dark `Color.FromArgb((int)(0xFF000000 + (rand.Next(0xFFFFFF) & 0x7F7F7F)))` – abenci Jun 20 '18 at 15:40
  • Huh, you're right. Wouldn't expect that to work... Nice. – Stevo Jun 20 '18 at 15:43
4

You could just use the overload that takes the individual color components. You're trading off 2 calls to FromArgb() for 3 random numbers.

var rand = new Random();
Color c = Color.FromArgb(rand.Next(256), rand.Next(256), rand.Next(256));
Khaledonia
  • 2,054
  • 3
  • 15
  • 33
itsme86
  • 19,266
  • 4
  • 41
  • 57
1

There is method, that accepts only one argument:

Color c = Color.FromArgb(rand.Next(int.MaxValue) | (0xFF0000));

0xFF0000 - to create solid color (alpha = 255)

Backs
  • 24,430
  • 5
  • 58
  • 85
1

I created library (MIT license) for work with colors. Also I added implementation with random generator.

Examples:

Get random color:

RGB rgb = ColorGenerator.GetRandomColor<RGB>();
HEX hex = ColorGenerator.GetRandomColor<HEX>();
CMYK cmyk = ColorGenerator.GetRandomColor<CMYK>();

Another methods:

GetLightRandomColor<type>();
GetDarkRandomColor<type>();
GetRedRandomColor<type>();
GetGreenRandomColor<type>();
GetBlueRandomColor<type>();

Links: Github and NuGet.

progm
  • 2,782
  • 3
  • 14
  • 32