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;
}
});
3
Answers
Try this:
Your formula treats
1
as 100%, but your Color functions are returning / expecting values in the range of[0, 255]
, which means that255 = 100%
.You have to use the following formula:
newColor = (colorA * opacityA + colorB * (255 - opacityA)) / 255
Example:
In the range of
[0,255]
, 25% are equal to 63. The result of this example should be 63 since
foreground * 25% + background * 75%
is63
.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, not0xFFFF7F7F
.As @frarugi87 said in his answer, you first have to set red’s alpha channel to 50%.
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:
There can be rounding issues, but… this should work.
Just another remark:
Color.RED
has a 255 alpha channel. This means thatremoveAlpha(red, 0xFFFFFFFF)
returns red itself, not 0xFFFF7F7F. In order to get that value you should write(or some close value)