Reputation: 1304
I'm trying some algorithms to write a method to remove alpha value from a color and give its identical rgb values but seems my test is always failing. I belive it is called alpha blending? I'm not sure. This is the algorithm I use for converting.
public static int removeAlpha(int foreground, int background) {
int redForeground = Color.red(foreground);
int redBackground = Color.red(background);
int greenForeground = Color.green(foreground);
int greenBackground = Color.green(background);
int blueForeground = Color.blue(foreground);
int blueBackground = Color.blue(background);
int alphaForeground = Color.alpha(foreground);
int redNew = (redForeground * alphaForeground) + (redBackground * (1 - alphaForeground));
int greenNew = (greenForeground * alphaForeground) + (greenBackground * (1 - alphaForeground));
int blueNew = (blueForeground * alphaForeground) + (blueBackground * (1 - alphaForeground));
return Color.rgb(redNew, greenNew, blueNew);
}
And the test like this
@Test
public void removeAlpha() {
int red = Color.RED;
Assert.assertEquals(0xFFFF7F7F, Heatmap.removeAlpha(red, 0xFFFFFFFF));
}
junit.framework.AssertionFailedError:
Expected :-32897
Actual :-258
when i draw red in photoshop and set the opacity to 50%, it gives me 255,127,127 rgb which seems identical to 50% opaque pure red. I think there the algorithm is false. Any helps would be appreciated.
Edit: Here are the mock Color:
PowerMockito.mockStatic(Color.class);
PowerMockito.when(Color.rgb(Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt())).thenAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
int red = (int) invocation.getArguments()[0];
int green = (int) invocation.getArguments()[1];
int blue = (int) invocation.getArguments()[2];
return (0xFF << 24) | (red << 16) | (green << 8) | blue;
}
});
PowerMockito.when(Color.alpha(Mockito.anyInt())).thenAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
return ((int)invocation.getArguments()[0])>>>24;
}
});
PowerMockito.when(Color.red(Mockito.anyInt())).thenAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
return (((int)invocation.getArguments()[0])>>16) & 0xFF;
}
});
PowerMockito.when(Color.green(Mockito.anyInt())).thenAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
return (((int)invocation.getArguments()[0])>>8) & 0XFF;
}
});
PowerMockito.when(Color.blue(Mockito.anyInt())).thenAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
return (int)invocation.getArguments()[0] & 0xFF;
}
});
Upvotes: 2
Views: 3764
Reputation: 18044
Your formula treats 1
as 100%, but your Color functions are returning / expecting values in the range of [0, 255]
, which means that 255 = 100%
.
You have to use the following formula:
newColor = (colorA * opacityA + colorB * (255 - opacityA)) / 255
Example:
foreground = 256
background = 0
foreground-alpha = 25%
In the range of [0,255]
, 25% are equal to 63. T
he result of this example should be 63 since foreground * 25% + background * 75%
is 63
.
So in order to get the 75%, you need 100% - 25% = 256 - 63 = 193
Second problem:
Your test case is wrong. You are taking 100% red
+ 100% white
which should result in 100% red, not 0xFFFF7F7F
.
As @frarugi87 said in his answer, you first have to set red's alpha channel to 50%.
Upvotes: 2
Reputation: 2880
NOTE: I'm not really into java, so I can be wrong. I'm just using common programming notions, so some tweakings can be needed.
I think you are messing with data types... You are getting the integer representation of your color, i.e. 0-255, and multiplying it as if it was a 0-1 representation. Try this:
double alphaForeground = ((double)Color.alpha(foreground)) / 255.0;
int redNew = ((int)round((redForeground * alphaForeground) + (redBackground * (1 - alphaForeground))));
int greenNew = ((int)round((greenForeground * alphaForeground) + (greenBackground * (1 - alphaForeground))));
int blueNew = ((int)round((blueForeground * alphaForeground) + (blueBackground * (1 - alphaForeground))));
There can be rounding issues, but... this should work.
Just another remark: Color.RED
has a 255 alpha channel. This means that removeAlpha(red, 0xFFFFFFFF)
returns red itself, not 0xFFFF7F7F. In order to get that value you should write
int red = Color.RED;
red.alpha = 0x80;
(or some close value)
Upvotes: 2