what I'm trying to do is fill an array with an rainbow gradient. The array should hold 256 entries an filled with the hex value of colors.
like:
array[0] = 0xFF0000 //red
...
array[85] = 0xFFA200 //orange
...
array[170] = 0x00AF0F //green
...
array[255] = 0xAE00FF //purple
because I do not want to assign all 256 colors "by hand" to the array I'm looking for an dynamicly approach for that. It's not necessarily need to be the ranbow displayed above. The picture is just for demonstration purpose.
Any suggestions how to do things like this in an (preferably) short code snipit avoiding a couple of nested for loops?
We want to go from:
red -> yellow
yellow -> green
green -> cyan
cyan -> blue
blue -> magenta
magenta -> red
In each pass, one of the red
, green
, blue
components is always 0
, the second is 255
, and the third is increasing or decreasing between 0
and 255
.
In other words:
{255, 0, 0} -> {255, 255, 0} grn increases to 255
{255, 255, 0} -> { 0, 255, 0} red decreases to 0
{0 , 255, 0} -> { 0, 255, 255} blu increases to 255
{0 , 255, 255} -> { 0, 0, 255} grn decreases to 0
{0 , 0, 255} -> {255, 0, 255} red increases to 255
{255, 0, 255} -> {255, 0, 0} blu decreases to 0
This produces 256 * 6 colors, we may not want all of those colors, so it has to be normalized. This can be done with following code:
//input: ratio is between 0.0 to 1.0
//output: rgb color
uint32_t rgb(double ratio)
{
//we want to normalize ratio so that it fits in to 6 regions
//where each region is 256 units long
int normalized = int(ratio * 256 * 6);
//find the region for this position
int region = normalized / 256;
//find the distance to the start of the closest region
int x = normalized % 256;
uint8_t r = 0, g = 0, b = 0;
switch (region)
{
case 0: r = 255; g = 0; b = 0; g += x; break;
case 1: r = 255; g = 255; b = 0; r -= x; break;
case 2: r = 0; g = 255; b = 0; b += x; break;
case 3: r = 0; g = 255; b = 255; g -= x; break;
case 4: r = 0; g = 0; b = 255; r += x; break;
case 5: r = 255; g = 0; b = 255; b -= x; break;
}
return r + (g << 8) + (b << 16);
}
Usage:
double range = 500.0;
for (double i = 0; i < range; i++)
{
uint32_t color = rgb(i / range);
...
}
Output:
One way to do what you want is to interpolate each of the component colours (RGB) from their starting to their ending value. This means using steps that are of as equal size as possible.
To get the RGB components some bit manipulation is necessary. This code gives you the three component colours for a fully-stated colour fullColour
:
std::array<float, 3> colourRGB;
for(unsigned i = 0; i<3; ++i) {
colourRGB[i] = (fullColour & (0xFF << (i*8))) >> (i*8);
You use this to get the RGB array of your starting and ending colour (for example, for the red-orange range in your example this would be the RGB breakdown of 0xFF0000
and 0xFFA200
). For each component you then work out the step size required for each step in your 'fade'. This is given by the total change in that component divided by the total number of steps. For the red-orange example the step size for the Red component would thus be 1.91 (because you have 86 steps and a total change of (0xA2
- 0x00
, or 162 in decimal, yielding 162/86=1.91). The step sizes for the other components are zero as they don't change.
You can then iterate over the steps, at each one adding another step size and rounding to get the new value for each component. For example, at step s
:
red[s] = startingRed + ((float)s * stepSizeRed)
To re-combine your rgb values back into a full colour, just apply some bit manipulation again:
fullColour[s] = 0;
for(unsigned i=0; i<3; ++i)
fullColour[s] += rgbComponent[s][i] << (i*8);
This approach yields the required 'fade' from any starting to any ending colour in any number of steps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With