Java: RGB, HSL, and subtractive color mixing - java

I have an application that requires me to mix two colors subtractively (as in mix them to get a result as if I has mixed two pigments). Just adding the RGB's doesn't work as RGB values operate additively, whereas pigments operate subtractively.
So what I'm trying to do is use HSL values. I convert the RGB to HSL using one of Java's Color Methods. I'm seeing colors as degrees on a wheel. Red has a hue value of 0, Green is 0.333, and Blue is 0.666. Since this is a wheel Red can also be 1, and green can also be 1.333.
So what I'm doing is averaging the hue values (as I'm mixing the "pigments" in equal ratios) along the shortest distance on the wheel. To explain, mixing blue (0.666) and red (0 or 1) would evaluate to 0.833, not 0.666. In my implementation, I account for this or at least try to, but there are some bugs...
public static Color addColors(Color a, Color b) {
float[] aHSB = toHSB(a);
float[] bHSB = toHSB(b);
double aHue = aHSB[0];
double bHue = bHSB[0];
System.out.println(aHue * 360 + ", " + bHue * 360);
double c = 0;
if (Math.abs(Math.abs(aHue - bHue) - 0.5) < 0.01) {
return Color.BLACK; //to account for complimentary colors
}
if (Math.abs(aHue - bHue) > 0.51) {
c = 0.5;
}
float temp = (float) (((aHue + bHue) / 2) % 1 + c);
Color d = new Color(Color.HSBtoRGB(temp,
(aHSB[1] + bHSB[1]) / 2,
(aHSB[2] + bHSB[2]) / 2));
return d;
}
When I mix Blue and Yellow, I get a green-ish tint. When I mix Red and Yellow, I get an orange, but when I mix orange and blue, I get a pink. I imagine there a problems with what I'm doing.
Are there better ways to mix colors given RGB values that yield results that match pigment mixing? Or am I on a (semi) right track and have just made a mistake?

Related

What's the best way to implement a color cycling background?

What's the best way to cycle the color of a background smoothly (as well as other things) using cos or sin in java, without using more than one file? I've tried using randomness and increasing each individual r, g, and b value separately to make this look kind of normal, but it's jittery, not smooth, and the colors are horrid. Right now, it's just plain white. I included only the necessary code, and I am using Processing 3.
//background
int bg1 = 255; //r
int bg2 = 255; //g
int bg3 = 255; //b
void draw() {
fill(bg1,bg2,bg3);
}
You've got the general idea down. It's a three-step process:
Step 1: Declare variables at the top of your sketch.
Step 2: Use those variables to draw your scene.
Step 3: Change those variables over time.
This is the basic approach to create any animation in Processing. Here is a tutorial with more information.
Here is a small example that shows a window that cycles between white and black:
float c = 0;
float cChange = 1;
void draw(){
background(c);
c += cChange;
if(c < 0 || c > 255){
cChange *= -1;
}
}
You would need to do something similar, but with 3 color values instead of 1. Note that I'm only changing the color by a small amount each time, which makes it appear smooth instead of jittery.
If you're still having trouble, please post an updated MCVE in a new question and we'll go from there. Good luck.
If you specifically want to use a sine wave as input rather than the sawtooth wave then you need to map your input (e.g. time) to some color range. For example:
every 2000 milliseconds value increases from 0 to 2.0
value ranges from -1 to 1.
the output of sin(value) ranges from -1 to 1.
map the output to a color range.
map() works well for mapping values, but you can also use colorMode() for mapping color ranges -- so rather than moving your sine output values around, just make your output 0-2.0 and set the max RGB or HSB value to 2.0 rather than 255.
Here are some examples, all running simultaneously in one sketch:
float val;
float out;
void draw() {
background(0);
val = TWO_PI * (millis()%2000)/2000.0; // every 2000 milliseconds value increases from 0 to 2PI
out = sin(val);
// white-black (256-0)
pushStyle();
fill(128 + 128*out);
rect(0,0,50,50);
popStyle();
// red-black (255-0)
pushStyle();
colorMode(RGB, 255);
fill(255*(out+1), 0, 0);
rect(50,0,50,50);
popStyle();
// hue rainbow (0-2)
pushStyle();
colorMode(HSB, 2.0);
fill(out+1, 2, 2);
rect(0,50,50,50);
popStyle();
// hue blue-green (3 to 5 / 9)
pushStyle();
colorMode(HSB, 9);
fill(out+4, 9, 9);
rect(50,50,50,50);
popStyle();
translate(width/2,height/2 - out * height/2);
ellipse(0,0,10,10);
}
Don't understand what you mean by cos and sin in relation to background color. But maybe something like this is what you want?
void draw(){
int H = frameCount%1536;
background(color(abs(H-765)-256,512-abs(H-512),512-abs(H-1024)));
}

How do I apply a blue to yellow gradient on a bitmap through code, brightness as factor

I need to apply a blue to yellow gradient to a bitmap.
The factor here is the brightness.
The dark areas of the photo need to be blueish and the brightest area's yellow.
So the brightness of every pixel needs to be taken as a factor.
Can someone help me how to accomplish this in c++ or java?
The input is an array of rgb integer values of the original photo.
Sounds a bit like a homework question, but here's the general idea, or at least, how I would do it.
For each pixel, calculate the average brightness, so add R G and B together then divide by 3 to get the result (you'll need to use a variable greater than 8 bits here!).
Now you have a value back in the range of 0-255 indicating the brightness of the pixel (there are various ways to calculate brightness but this will do for now).
Full blue is (0,0,255), full yellow is (255,255,0) — so you need to interpolate between these values (we'll use linear interpolation here):
If your brightness is 50 for instance, it's ~20% of 255, so you want a colour that's 80% blue and 20% yellow. You can calculate the valye for the red channel like so:
R = (brightness / max) * (R in Yellow - R in Blue);
With similar calculations for the other channels, so for our pixel with a brightness of 50 we'd do:
R = (50 / 255) * 255;
G = (50 / 255) * 255;
Of course, we can't have negative values, and using B in Yellow - B in Blue idea isn't going to cut it for the blue channel, you need to invert the interpolation. By taking our 0.2 and subtracting it from 1 we can work through the range 0-255 in the other direction:
B = (1 - (50 / 255)) * 255;
Extra note: To work with something like this in C++ I'd suggest using SDL, it's nice and easy this kind of thing.
If I understood you correctly, the following (applied to all pixels individually) should do what you want:
// max_value gives the maximum allowed value for red, green and blue; that is,
// if red, green and blue are all equal to max_value, you have full white)
change_pixel(int& red, int& green, int& blue, int max_value)
{
blue = (red+green+blue)/3;
red = green = (max_value-blue);
}

Java - Colouring image

I've tried to create colored gray image with given colour but I always failed. I know I have to use BufferedImage and load all pixels of source image into array, but I don't know how to work with color value, so there always were some weird colors after multiplying source color and given color (for the record I've got code to get int of color).
Thanks for any help.
e.g. http://dl.dropbox.com/u/17664230/ruka.png
Are you trying to change image gray channel (white-black) to (some_color-black) ?
public void recolor(BufferedImage source, BufferedImage destination, int color) {
WritableRaster destinationRaster = destination.getRaster();
Raster sourceRaster = source.getRaster();
//red, green, blue
int channels = new int[]{color & 0xFF0000, color & 0xFF00, color & 0xFF};
for (int channel=0; channel<3; channel++){
for (int y=0; y<sourceRaster.getHeight(); y++) {
for (int x=0; x<rangeSlicer.getWidth(); x++) {
pixel = sourceRaster.getSample(x, y, channel);
pixel = Math.round((double)pixel/255.0 * channels[channel]);
destinationRaster.setSample(x, y, channel, pixel);
}
}
}
}
Not sure if this compiles, but idea is that. For more effective program check RescaleOp.
I would define some alpha = 0.2 and do:
given gray #777777
make redder with:
new red = 77 * (1.0 + alpha)
new green = blue = 77 * (1.0 - alpha)
Actually, you have to be careful and make sure to truncate (1 + alpha) and (1 - alpha) such that you only use values between 0 and 1.0.
Use Math.min(0.0, 1.0 - alpha) instead of (1.0 - alpha) and Math.max(1.0, 1.0 + alpha) instead of (1.0 + alpha).
With this formula, if you are given black (#000000), it stays black. If you are given white (#FFFFFF), it ends up pink (#FFCCCC). If you set alpha = 1.0, white would end up pure red (#FF0000).
You could come up with different color scaling formulas, but I guess you should make sure that your transformation always keeps black as pure black.

Remove the alpha from a colour but retain its texture

I have a colour #6A8F6509
I wish to remove the Alpha part of that colour and be left with only RGB components (i.e. #RRGGBB).
The resulting colour must look identical to the initial one without the transparency.
How do I go about this on Android's Java?
Update: The initial background is always white (#FFF)
int newColor = oldColor | 0xFF000000;
If you want to do it with the code you can try following code:
static int stripAlpha(int color){
return Color.rgb(Color.red(color), Color.green(color), Color.blue(color));
}
For each color:
C' = C(a/255) + 255(1-(a/255))
So for #6A8F6509:
R' = 143(106/255) + 255(1-(106/255) = (approx) 208
G' = 101(106/255) + 255(1-(106/255) = (approx) 191
B' = 9(106/255) + 255(1-(106/255) = (approx) 153
So your answer should be: #D0BF99, if my math is correct. This only applies to an all white background as well - for a non-white background, the second addend should have the appropriate corresponding color value of the background instead of 255.
-- EDIT --
Looking at the formula again, I'm not entirely sure whether the formula gets applied to each color or the entire color (a quick visual test should be able to tell you - I'm guessing per color). I should point out that this is the formula direct from the Alpha Compositing wiki page:
http://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending
try
#FF8F6509
the first two digits indicate the alpha value of any color.
Calculate the way a semi-transparent color will look when placed on top of another color.
Kotlin color extension:
#ColorInt
fun #receiver:ColorInt Int.removeAlpha(#ColorInt underlyingColor: Int): Int {
return ColorUtils.blendARGB(this, underlyingColor, alpha / 255F)
}
Usage:
Color.parseColor("#6A8F6509").removeAlpha(underlyingColor = Color.WHITE)
Jetpack Compose version is
Color(0x6A8F6509).compositeOver(Color.White)
I'll admit to not knowing Android's Java, but can't you just change the color from #6A8F6509 to #FF8F6509? The in most modern programming languages, color is typically encoded as ##AARRGGBB.
AA = Hex of the Alpha, ranging from 0 (hex 00), full transparent, to 255 (hex FF), no transparency.
RR = Hex of the red part of the color. Similar to above, 0 is no red, 255 is full red.
GG = Hex of green.
BB = Hex of blue.
-Edit-
Based on your update, you can do this to get the color:
For each of the red, green, and blue:
get the difference between the transparent color and the background color and multiply that by the alpha value. Divide that by 255 (hex FF, if you prefer). Finally, add that number to the transparent color's value.
In your case:
int transparentR = 0x8F;
int transparentG = 0x65;
int transparentB = 0x09;
int backgroundR = 0xFF;
int backgroundG = 0xFF;
int backgroundB = 0xFF;
int alpha = 0x6A;
int newR = transparentR + ( ( (backgroundR - transparentR) * alpha ) / 255 );
int newG = transparentR + ( ( (backgroundG - transparentG) * alpha ) / 255 );
int newB = transparentR + ( ( (backgroundB - transparentB) * alpha ) / 255 );
-Edit, again-
Ok, I've redone the formula based on LJ2's answer using Wikipedia's Alpha Blending function. and had a colleague confirm this with Paint.Net. This is the version where the background Color is fully opaque, and it's in C#, sorry:
int transparentColor2(int intFgColor, int intBgColor, int intAlpha)
{
double dAlpha = (Convert.ToDouble(intAlpha)/255.0);
double dFirst = Convert.ToDouble(intFgColor) * dAlpha;
double dSecond = Convert.ToDouble(intBgColor) * (1 - dAlpha);
return Convert.ToInt32(dFirst + dSecond);
}
do this for each R, G, and B value, and you should get the right answer. Incase Android's Java has different, double could be Double/Single/Float, and int could be Int32/Int16/Int64/Byte. I don't remember how to Convert between the two in Java, unfortunately.

Generating spectrum color palettes

Is there an easy way to convert between color models in Java (RGB, HSV and Lab).
Assuming RGB color model:
How do I calculate black body spectrum color palette? I want to use it for a heatmap chart.
How about single-wavelength spectrum?
Edit: I found that the ColorSpace class can be used for conversions between RGB/CIE and many other color models.
Java has built-in RGB to HSB conversion. Whenever I need a quick pallet of colors in Java I just do this:
public Color[] generateColors(int n)
{
Color[] cols = new Color[n];
for(int i = 0; i < n; i++)
{
cols[i] = Color.getHSBColor((float) i / (float) n, 0.85f, 1.0f);
}
return cols;
}
It is a quick and dirty hack (I would tweak the 'magic' numbers for your app), but for my simple uses it generates a nice bright pleasant pallet.
Maybe I'm not understanding your question, but you can't really generate a true black-body spectrum from an RGB output device. Limited color gamut would be an issue, if nothing else. If all you want is something that visually resembles a black-body spectrum, that's probably a lot easier.
As an approximation, ramp from (R,G,B) (0,0,0) to (255,0,0), then to (255,255,0), then to (255,255,255). That'd give you the dull-red to orange, to yellow, to white transition.
If you want something more scientific, the Wikipedia article on black body radiation has some plots of color vs temperature. Once you figure out the CIE coordinates, you can translate those to RGB in your favorite color space.
Edit: found some other online references:
What color is the Sun?
What color is a blackbody?
You can build such a palette using the HSV color-model. That's easy once you have the HSV to RGB code in place and play around with the numbers for some minutes.
However, I think it's not worth it to add the code to your project just to generate a little palette.
It's much easier and less work to extract the palettes you need from a file and add them as a static array.
Photoshop let's you edit palettes and comes with a very nice black body palette as a preset.
You can simply save these as a .act file. The file itself is just a simple 256 color á 3 byte file (order is read, green, blue. 8 bits per channel).
You can generate this color spectrum https://i.stack.imgur.com/ktLmt.jpg
using the following code:
public void render(Screen screen) {
int green = 255;
int red = 0;
for (int i = 0; i <= 255 * 2; i++) {
int rate = i / 255;
screen.fillRect((x + (i * width)/6), y, width, height, new Color(red, green, 0));
red += 1 - rate;
green -= rate;
}
}
This is a nice way to make a HSL color square in AS3.
/**
* Generate a BitmapData HSL color square (n x n) of hue
* At a low n dimension you get cool blocky color palettes (e.g. try n=10)
*/
function generateColorSquare(n:uint, hue:uint):BitmapData
{
var bd:BitmapData = new BitmapData(n, n, false, 0xFFFFFF);
for (var i:uint=n*n; i > 0; i--)
{
bd.setPixel(i % n, Math.floor(i / n), HSBColor.convertHSBtoRGB(hue, i / (n*n), (1/n) * (i % n) ));
}
return bd;
}

Categories