Calculate a color fade - colors

Given two colors and n steps, how can one calculate n colors including the two given colors that create a fade effect?
If possible pseudo-code is preferred but this will probably be implemented in Java.
Thanks!

Divide each colour into its RGB components and then calculate the individual steps required.
oldRed = 120;
newRed = 200;
steps = 10;
redStepAmount = (newRed - oldRed) / steps;
currentRed = oldRed;
for (i = 0; i < steps; i++) {
currentRed += redStepAmount;
}
Obviously extend that for green and blue.

There are two good related questions you should also review:
Generating gradients programatically?
Conditional formatting — percentage to color conversion
Please note that you're often better off doing this in the HSV color space rather than RGB - it generates more pleasing colors to the human eye (lower chance of clashing or negative optical properties).
Good luck!
-Adam

If you want a blend that looks anything like most color picker GUI widgets, you really want to translate to HSL or HSV. From there, you're probably fine with linear interpolation in each dimension.
Trying to do any interpolations directly in RGB colorspace is a bad idea. It's way too nonlinear (and no, gamma correction won't help in this case).

For those looking for something they can copy and paste. Made a quick function for RGB colors. Returns a single color that is the amount of ratio closer to rgbColor2.
function fadeToColor(rgbColor1, rgbColor2, ratio) {
var color1 = rgbColor1.substring(4, rgbColor1.length - 1).split(','),
color2 = rgbColor2.substring(4, rgbColor2.length - 1).split(','),
difference,
newColor = [];
for (var i = 0; i < color1.length; i++) {
difference = color2[i] - color1[i];
newColor.push(Math.floor(parseInt(color1[i], 10) + difference * ratio));
}
return 'rgb(' + newColor + ')';
}

The quesiton is what transformation do you want to occur? If you transpose into the HSV colourspace and given
FF0000 and 00FF00
It will transition from red through yellow to green.
However, if you define "black" or some other shade as being the mid-point of the blend, you have to shade to that colour first ff0000->000000->00ff00 or via white : ff0000 -> ffffff -> 00ff00.
Transforming via HSV however can be fun because you have to use a bit of trig to map the circular map into the vector components.

The easiest thing to do is linear interpolation between the color components (see nickf's response). Just be aware that the eye is highly nonlinear, so it won't necessarily look you're making even steps. Some color spaces attempt to address this (CIE maybe?), so you might want to transform into another color space first, interpolate, then transform back to RGB or whatever you're using.

How about this answer
- (UIColor *)colorFromColor:(UIColor *)fromColor toColor:(UIColor *)toColor percent:(float)percent
{
float dec = percent / 100.f;
CGFloat fRed, fBlue, fGreen, fAlpha;
CGFloat tRed, tBlue, tGreen, tAlpha;
CGFloat red, green, blue, alpha;
if(CGColorGetNumberOfComponents(fromColor.CGColor) == 2) {
[fromColor getWhite:&fRed alpha:&fAlpha];
fGreen = fRed;
fBlue = fRed;
}
else {
[fromColor getRed:&fRed green:&fGreen blue:&fBlue alpha:&fAlpha];
}
if(CGColorGetNumberOfComponents(toColor.CGColor) == 2) {
[toColor getWhite:&tRed alpha:&tAlpha];
tGreen = tRed;
tBlue = tRed;
}
else {
[toColor getRed:&tRed green:&tGreen blue:&tBlue alpha:&tAlpha];
}
red = (dec * (tRed - fRed)) + fRed;
green = (dec * (tGreen - fGreen)) + fGreen;
blue = (dec * (tBlue - fBlue)) + fBlue;
alpha = (dec * (tAlpha - fAlpha)) + fAlpha;
return [UIColor colorWithRed:red green:green blue:blue alpha:alpha];
}

Related

Scrolling through colors effect in RGB

I want to create a colour scroller effect. I have a function that I give it RGB values (eg. setColor(189,234,45)) and I want to change the colour rapidly but I don't want to get many repeats to create an effect of scrolling through the colours.
I have tried tried the following but it doesn't quite generate the effect that I am looking for.
for (int i = 0; i < 256; i++) {
for (int j = 0; j < 256; j++) {
for (int k = 0; k < 256; k++) {
setColor(i, j, k);
}
}
}
I wanted to know if anyone knows how the colour scroller's colours are arranged next to each other. The arrangement I am looking for looks like the scroll on the right.
The colors you are working with are represented as R,G,B (red green blue) values. However, another
way to think about color is hue, saturation, value. In the scroll image you are trying to emulate,
it is the hue that is changing - the saturation and value (brightness) are unaffected.
Here is a function that happens to make a hue-cycle gradient like the one in the image you linked to:
int n = 256; // number of steps
float TWO_PI = 3.14159*2;
for (int i = 0; i < n; ++i) {
int red = 128 + sin(i*TWO_PI/n + 0) + 127;
int grn = 128 + sin(i*TWO_PI/n + TWO_PI/3) + 127;
int blu = 128 + sin(i*TWO_PI/n + 2*TWO_PI/3) + 127;
setColor(red, grn, blu);
}
To understand how that function works, I recommend that you read my color tutorial that GreenAsJade linked to.
However, that kind of gradient function isn't quite what you need, because you want to start from a particular color you are passing in, and then go to the next color in the sequence. It's much easier to do this kind of thing if you represent your colors as HSV triplets (or HSB triplets), instead of RGB triplets. Then you can manipulate just the hue component, and get those kind of rainbow effects. In helps to have a set of function that can convert from RGB to HSV and back again.
This site contains a bunch of color conversion source code, including the ones you need for those conversions. Using the two conversion functions supplied on that page, your code might look like:
void cycleMyColor(int *r, int *g, int *b) {
float h,s,v, fr,fg,fb;
RGBtoHSV(*r/255.0,*g/255.0,*b/255.0,&h,&s,&v);
h += 1/256.0; // increment the hue here
h -= (int) h; // and cycle around if necessary
HSVtoRGB(&fr,&fg,&fb,h,s,v);
*r = fr*255; *g = fg*255; *b = fb*255;
setColor(*r,*g,*b);
}
This code is a little more complicated than it needs to be because the color conversions on that site use floating point color components that go from 0-1, instead of integers that go from 0-255, as you were using, so I'm spending a few lines converting between those two representations. You may find it simpler to just keep your color in HSB space, and then convert to RGB when you want to display it.
As you mentioned in your edit, you don't like the sequence of colours, because you start from black an end at white, instead of starting at one end of the rainbow and going to the other.
So you are going to need to work out a sequence of RGB that goes from blue through green and yellow to red. That means you need to start with (0,0,255) and end at (255, 0, 0), and don't pass through (255,255,255) or (0,0,0) - in a nutshell, that's how its done.
There are many ways you could do this and get a pleasing effect - beyond the scope of an answer here. This article explores it in depth:
http://krazydad.com/tutorials/makecolors.php

Unity Shader RGBA Color mask

I am working on a shader for Unity in which I want to change the color based on an mask image. In this mask image the RGB channels stand for a color that can be choosen in the shader. The idea behind the shader is that it is easy to change the look of an object without having to change the texture by hand.
Shader "Custom/MultiColor" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_MaskTex ("Mask area (RGB)", 2D) = "black" {}
_ColorR ("Red Color", Color) = (1,1,1,1)
_ColorG ("Green Color", Color) = (1,1,1,1)
_ColorB ("Blue Color", Color) = (1,1,1,1)
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Lambert
sampler2D _MainTex;
sampler2D _MaskTex;
half4 _ColorR;
half4 _ColorG;
half4 _ColorB;
half4 _MaskMult;
struct Input {
float2 uv_MainTex;
};
void surf (Input IN, inout SurfaceOutput o) {
half4 main = tex2D (_MainTex, IN.uv_MainTex);
half4 mask = tex2D (_MaskTex, IN.uv_MainTex);
half3 cr = main.rgb * _ColorR;
half3 cg = main.rgb * _ColorG;
half3 cb = main.rgb * _ColorB;
half r = mask.r;
half g = mask.g;
half b = mask.b;
half minv = min(r + g + b, 1);
half3 cf = lerp(lerp(cr, cg, g*(r+g)), cb, b*(r+g+b));
half3 c = lerp(main.rgb, cf, minv);
o.Albedo = c.rgb;
o.Alpha = main.a;
}
ENDCG
}
FallBack "Diffuse"
}
The problem with the shader is the blending between the masked color based on the green and blue channel. Between colors defined in the color supposted to be from the red region is visible. Below a sample is visable.
The red color is create by the red mask, green by the green mask and desert yellow by the blue region. I do not know why this happens or how to solve this problem.
Best guess: anti-aliasing or image compression. Aliasing (on the brush your using) will cause an overlap in the color channels, causing them to mix. Compression usually works by averaging each pixel's color info based on the colors around it (jpeg is especially notorious for this).
Troubleshoot by using a straight pixel based brush (no aliasing, no rounded edges) in Photoshop (or whatever image suite you're using) and/or try changing the colors through your shader and see how they mix- doing either should give you a better idea of what's going on under the hood. This combined with an lossless/uncompressed image-type, such as .tga should help, though they may use more memory.

Fastest formula to get Hue from RGB

If you are given red, green, and blue values that range from 0-255, what would be the fastest computation to get just the hue value? This formula will be used on every pixel of a 640x480 image at 30fps (9.2 million times a second) so every little bit of speed optimization helps.
I've seen other formulas but I'm not happy with how many steps they involve. I'm looking for an actual formula, not a built in library function.
Convert the RGB values to the range 0-1, this can be done by dividing the value by 255 for 8-bit color depth (r,g,b - are given values):
R = r / 255 = 0.09
G = g / 255 = 0.38
B = b / 255 = 0.46
Find the minimum and maximum values of R, G and B.
Depending on what RGB color channel is the max value. The three different formulas are:
If Red is max, then Hue = (G-B)/(max-min)
If Green is max, then Hue = 2.0 + (B-R)/(max-min)
If Blue is max, then Hue = 4.0 + (R-G)/(max-min)
The Hue value you get needs to be multiplied by 60 to convert it to degrees on the color circle. If Hue becomes negative you need to add 360 to, because a circle has 360 degrees.
Here is the full article.
In addition to Umriyaev's answer:
If only the hue is needed, it is not required to divide the 0-255 ranged colours with 255.
The result of e.x. (green - blue) / (max - min) will be the same for any range (as long as the colours are in the same range of course).
Here is the java example to get the Hue:
public int getHue(int red, int green, int blue) {
float min = Math.min(Math.min(red, green), blue);
float max = Math.max(Math.max(red, green), blue);
if (min == max) {
return 0;
}
float hue = 0f;
if (max == red) {
hue = (green - blue) / (max - min);
} else if (max == green) {
hue = 2f + (blue - red) / (max - min);
} else {
hue = 4f + (red - green) / (max - min);
}
hue = hue * 60;
if (hue < 0) hue = hue + 360;
return Math.round(hue);
}
Edit: added check if min and max are the same, since the rest of the calculation is not needed in this case, and to avoid division by 0 (see comments)
Edit: fixed java error
Probably not the fastest but this is a JavaScript function that you can try directly in the browser by clicking the "Run code snippet" button below
function rgbToHue(r, g, b) {
// convert rgb values to the range of 0-1
var h;
r /= 255, g /= 255, b /= 255;
// find min and max values out of r,g,b components
var max = Math.max(r, g, b), min = Math.min(r, g, b);
// all greyscale colors have hue of 0deg
if(max-min == 0){
return 0;
}
if(max == r){
// if red is the predominent color
h = (g-b)/(max-min);
}
else if(max == g){
// if green is the predominent color
h = 2 +(b-r)/(max-min);
}
else if(max == b){
// if blue is the predominent color
h = 4 + (r-g)/(max-min);
}
h = h*60; // find the sector of 60 degrees to which the color belongs
// https://www.pathofexile.com/forum/view-thread/1246208/page/45 - hsl color wheel
// make sure h is a positive angle on the color wheel between 0 and 360
h %= 360;
if(h < 0){
h += 360;
}
return Math.round(h);
}
let gethue = document.getElementById('gethue');
let r = document.getElementById('r');
let g = document.getElementById('g');
let b = document.getElementById('b');
r.value = Math.floor(Math.random() * 256);
g.value = Math.floor(Math.random() * 256);
b.value = Math.floor(Math.random() * 256);
gethue.addEventListener('click', function(event) {
let R = parseInt(r.value)
let G = parseInt(g.value)
let B = parseInt(b.value)
let hue = rgbToHue(R, G, B)
console.log(`Hue(${R}, ${G}, ${B}) = ${hue}`);
});
<table>
<tr><td>R = </td><td><input id="r"></td></tr>
<tr><td>G = </td><td><input id="g"></td></tr>
<tr><td>B = </td><td><input id="b"></td></tr>
<tr><td colspan="2"><input id="gethue" type="button" value="Get Hue"></td></tr>
</table>
The web page Math behind colorspace conversions, RGB-HSL covers this however it contains what I believe is an error. It states for hue calculation to divide by max-min however if you divide by this fractional amount the value increases and easily exceeds the full expected range of -1 to 5. I found multiplying by max-min to work as expected.
Instead of this:
If Red is max, then Hue = (G-B)/(max-min)
If Green is max, then Hue = 2.0 + (B-R)/(max-min)
If Blue is max, then Hue = 4.0 + (R-G)/(max-min)
I suggest this:
If Red is max, then Hue = (G-B)*(max-min)
If Green is max, then Hue = 2.0 + (B-R)*(max-min)
If Blue is max, then Hue = 4.0 + (R-G)*(max-min)
You must specify which language and platform you're using because C#, Java and C are very different languages and the performance also varies among them and among the platforms. The question is currently too broad!!!
640×480 is not very large compared to current common resolutions, but "fastest" is subjective and you need to do careful benchmarking to choose which is best for your usecase. An algorithm that looks longer with many more steps isn't necessarily slower than a shorter one because instruction cycles are not fixed and there are many other factors that affect performance such as cache coherency and branch (mis)predictions.
For the algorithm Umriyaev mentioned above, you can replace the division by 255 with a multiplication by 1.0/255, that'll improve performance with a tiny acceptable error.
But the best way will involve vectorization and parallelization in some way because modern CPUs have multiple cores and also SIMD units to accelerate math and multimedia operations like this. For example x86 has SSE/AVX/AVX-512... which can do things on 8/16/32 channels at once. Combining with multithreading, hardware acceleration, GPU compute.... it'll be far better than any answers in this question.
In C# and Java there weren't many vectorization options in the past so with older .NET and JVM versions you need to run unsafe code in C#. In Java you can run native code through JNI. But nowadays all of them also had vectorized math support. Java had a new Vector API in JEP-338. In Mono you can use the vector type in the Mono.Simd namespace. In RyuJIT there's Microsoft.Bcl.Simd. In .NET 1.6+ there's System.Numerics which includes Vector and other
... SIMD-enabled vector types, which include Vector2, Vector3, Vector4, Matrix3x2, Matrix4x4, Plane, and Quaternion.
How to use the Intel AVX in Java?
Parallelism on a Single Core - SIMD with C#
SIMD in Depth - Performance and Cost in C# and C++
Will .NET ever do intelligent SIMD?
Using System.Numerics.Vector for Graphics Programming
System.Numerics.Vectors 'Vector<T>': is it basically just System.UInt128?
Performance Gains with Data Parallelism: Using SIMD Instructions from C#
You could use one of the mathematical techniques suggested here, but instead of doing it on every pixel, do it on a random sample of ~10% of the pixels. This is still very likely to have high accuracy and will be 10x as fast.

Converting images into a linear color palette with JS, losing colors

The project in question: https://github.com/matutter/Pixel2 is a personal project to replace some out of date software at work. What it should do is, the user adds an image and it generates a color palette of the image. The color palette should have no duplicate colors. (thats the only important stuff)
My question is: why do larger or hi-res or complex images not work as well? (loss of color data)
Using dropzone.js I have the user put a picture on the page. The picture is a thumbnail. Next I use jquery to find the src out of a <img src="...">. I pass that src to a function that does this
function generate(imgdata) {
var imageObj = new Image();
imageObj.src = imgdata;
convert(imageObj); //the function that traverses the image data pulling out RGB
}
the "convert" function pulls out the data fairly simply by
for(var i=0, n=data.length; i<n; i+=4, pixel++ ) {
r = data[i];
g = data[i+1];
b = data[i+2];
color = r + g + b; // format is a string of **r, g, b**
}
finally, the last part of the main algorithme filters out duplicate colors, I only want just 1 occurrence of each... here's the last part
color = monoFilter(color); // the call
function monoFilter(s) {
var unique = [];
$.each(s, function(i, el){
if($.inArray(el, unique) === -1) unique.push(el);
});
unique.splice(0,1); //remove undefine
unique.unshift("0, 0, 0"); //make sure i have black
unique.push("255, 255, 255"); //and white
return unique;
}
I'm hoping someone can help me identify why there is such a loss of color data in big files.
If anyone is actually interesting enough to look at the github, the relivent files are js/pixel2.js, js/dropzone.js, and ../index.html
This is probably the cause of the problem:
color = r + g + b; // format is a string of **r, g, b**
This simply adds the numbers together and the more pixels you have the higher risk you run to get the same number. For example, these colors generate the same result:
R G B
color = 90 + 0 + 0 = 90;
color = 0 + 90 + 0 = 90;
color = 0 + 0 + 90 = 90;
even though they are completely different colors.
To avoid this you can do it like this if you want a string:
color = [r,g,b].join();
or you can create an integer value of them (which is faster to compare with than a string):
color = (b << 16) + (g << 8) + r; /// LSB byte-order
Even an Euclidean vector would be better:
color = r*r + g*g + b*b;
but with the latter you risk eventually the same scenario as the initial one (but useful for nearest color scenarios).
Anyways, hope this helps.
"The problem was that I wasn't accounting for alpha. So a palette from an image that uses alpha would have accidental duplicate records."
I figured this out after finding this Convert RGBA color to RGB

How to create a hue (saturation) picker in cocos2d

I'm trying to create a simplified hue/saturation picker for cocos2d. I want to create a gradient and to pick from it. I need to recolor a black/white image gradient for every color like blue, red and others. So I need to create many gradients. I know that I should use some blend functions to achieve this.
But I'm still a little bit confused about what is the best way to proceed.
Should I use blend functions at all ?
My problem basically is that I use a gradient from black to transparent or to white but with
sprite.setColor(color);
I get a gradient from black to the desired color but I need a gradient from the desired darker color to white.
What you need to do is create a 2D gradient that goes from unsaturated to saturated left-to-right, and from dark to light bottom-to-top. I'd do it by creating a new bitmap (or if you're using OpenGL, a texture). I'd then color each pixel using the following pseudocode:
hue = <whatever the user set the hue to>
for (row = 0; row < height; row++)
{
for (col = 0; col < width; col++)
{
sat = col / width;
val = row / height;
rgb = HSVToRGB(hue, sat, value);
setPixel (col, row, rgb);
}
}

Resources