Page 1 of 2
alpha blending
Posted: Mon Jan 02, 2017 10:40 pm
by szhou42
I am trying to alpha blend the window with the background, using this two formulas
My blending code:
Code: Select all
uint32_t blend_colors(uint32_t color1, uint32_t color2) {
uint32_t alpha1 = GET_ALPHA(color1);
uint32_t red1 = GET_RED(color1);
uint32_t green1 = GET_GREEN(color1);
uint32_t blue1 = GET_BLUE(color1);
uint32_t alpha2 = GET_ALPHA(color2);
uint32_t red2 = GET_RED(color2);
uint32_t green2 = GET_GREEN(color2);
uint32_t blue2 = GET_BLUE(color2);
uint32_t r = (uint32_t)((alpha1 * 1.0 / 255) * red1);
uint32_t g = (uint32_t)((alpha1 * 1.0 / 255) * green1);
uint32_t b = (uint32_t)((alpha1 * 1.0 / 255) * blue1);
r = r + (((255 - alpha1) * 1.0 / 255) * (alpha2 * 1.0 / 255)) * red2;
g = g + (((255 - alpha1) * 1.0 / 255) * (alpha2 * 1.0 / 255)) * green2;
b = b + (((255 - alpha1) * 1.0 / 255) * (alpha2 * 1.0 / 255)) * blue2;
uint32_t new_alpha = (uint32_t)(alpha1 + ((255 - alpha1) * 1.0 / 255) * alpha2);
uint32_t color1_over_color2 = (new_alpha << 24) | (r << 16) | (g << 8) | (b << 0);
return color1_over_color2;
}
before blending:
after blending:
Is there anything wrong with my code, the results looks odd. I was expecting it to look... more pretty
Re: alpha blending
Posted: Mon Jan 02, 2017 11:40 pm
by SpyderTL
If your color1 is your window color, and color2 is your background color, then you can probably just ignore Alpha2, at least for now. Your background alpha shouldn't affect the colors of any "layers" above it.
As for your window color, all you need is the ratio of window color to background color. The ratio is the window alpha color divided by 255.
Then, you just add the 3 weighted color components.
Code: Select all
uint32_t blend_colors(uint32_t color1, uint32_t color2) {
uint32_t alpha1 = GET_ALPHA(color1);
uint32_t red1 = GET_RED(color1);
uint32_t green1 = GET_GREEN(color1);
uint32_t blue1 = GET_BLUE(color1);
uint32_t red2 = GET_RED(color2);
uint32_t green2 = GET_GREEN(color2);
uint32_t blue2 = GET_BLUE(color2);
float ratio = alpha1 / 255.0f;
uint32_t red = (red1 * ratio) + (red2 * (1 / ratio));
uint32_t green = (green1 * ratio) + (green2 * (1 / ratio));
uint32_t blue = (blue1 * ratio) + (blue2 * (1 / ratio));
uint32_t result = (255 << 24) | (red << 16) | (green << 8) | (blue << 0);
return result;
This should work fine for your case. Once you get that working, then you can add some logic for calculating the result alpha value, so that you can combine two non-background images, but it's a little more complicated, because you are going to have to make some decisions about how your alpha blending is going to work.
But let us know if that solves your immediate problem.
Re: alpha blending
Posted: Tue Jan 03, 2017 3:40 am
by dozniak
It _looks_ to me that you're picking background pixels from wrong locations. Are you sure you are getting the pixel data correctly? Esp from background window backing buffer.
Re: alpha blending
Posted: Tue Jan 03, 2017 7:59 am
by szhou42
Thanks SpyerTL and dozniak !
I just fixed it
The formulas were correct, like dozniak said, I actually fetched the pixels at wrong location.
It looks good now
Re: alpha blending
Posted: Tue Jan 03, 2017 8:25 am
by szhou42
Moving window is significantly slowed down since the color of the moving window needs to be recalculated again at every move.
Any ideas how to deal with this?
Re: alpha blending
Posted: Tue Jan 03, 2017 8:59 am
by FusT
I'd probably only drag an outline of the window and then repaint the entire thing once movement has stopped/mouse has been released.
Re: alpha blending
Posted: Tue Jan 03, 2017 1:41 pm
by SpyderTL
Yep. Alpha blending is noticeably slow, unless you can get some hardware acceleration working for your specific video card.
The CPU just isn't powerful enough to calculate that many pixels without the user noticing.
You may want to look into writing this method in ASM (or in an assembly block), and using SSE extensions, which should make this code run around 4x faster, or more.
Re: alpha blending
Posted: Wed Jan 04, 2017 6:38 am
by Sik
Just to make sure: it's not reading from the video memory but something from RAM, right? Because reading from video memory is the absolute worst thing you could ever do. And also make sure it's not just
the emulator being slow.
Otherwise yeah, alpha blending is slow. You can try to optimize the blending function (huh, is that integer converted to float with separate multiplication and division (which can't be optimized away unless you use -ffast-math) and then more float divisions and then converted back to integer,
on every pixel? x_x). Microoptimizing generally is not useful but in this case it'll be iterating over many thousands of pixels (or millions if the window is large enough) so you definitely want to speed it up as much as you can.
Re: alpha blending
Posted: Wed Jan 04, 2017 7:19 am
by dozniak
Afaik there are SSE/AVX functions to specifically help with this sort of thing (alpha blending), if you don't want to use asm you could use gcc intrinsics for that.
Re: alpha blending
Posted: Wed Jan 04, 2017 8:28 am
by Brendan
Hi,
Sik wrote:Otherwise yeah, alpha blending is slow. You can try to optimize the blending function (huh, is that integer converted to float with separate multiplication and division (which can't be optimized away unless you use -ffast-math) and then more float divisions and then converted back to integer, on every pixel? x_x). Microoptimizing generally is not useful but in this case it'll be iterating over many thousands of pixels (or millions if the window is large enough) so you definitely want to speed it up as much as you can.
I'm not sure that I really want to mention it (because I prefer "correct" over "fast"); but with 8-bit integer alpha it's much faster to divide by 256 (when correct code would divide by 255). This allows you to do something like "out_blue = (blue1 * alpha) + (blue2 * ~alpha)) >> 8;".
Another (even faster) hack is to only use "50% alpha", so you can do something like "out_blue = (blue1 + blue2) >> 1;".
For 100% correct you need to take gamma into account and use "pow()". In practice this becomes something like "out_blue = gammaTable[ reverseGammaTable[blue1] * alpha + reverseGammaTable[blue2] * (1.0 - alpha) ];".
Cheers,
Brendan
Re: alpha blending
Posted: Wed Jan 04, 2017 10:28 am
by dchapiesky
dozniak wrote:Afaik there are SSE/AVX functions to specifically help with this sort of thing (alpha blending), if you don't want to use asm you could use gcc intrinsics for that.
SSE...
http://wm.ite.pl/articles/sse4-alphaover.html
AVX...
https://breeswish.org/blog/2015/07/26/avx2-alphablend/
Re: alpha blending
Posted: Wed Jan 04, 2017 10:48 am
by dchapiesky
more speed... MIT licensed.... well written...
https://github.com/skywind3000/BasicBit ... p_SSE2.cpp
worth a look if only for the 128bit memcpy
Re: alpha blending
Posted: Wed Jan 04, 2017 11:02 am
by Sik
(writing ~ instead of the actual ASCII symbol because the forum's font makes it look like - instead)
Brendan wrote:I'm not sure that I really want to mention it (because I prefer "correct" over "fast"); but with 8-bit integer alpha it's much faster to divide by 256 (when correct code would divide by 255). This allows you to do something like "out_blue = (blue1 * alpha) + (blue2 * ~alpha)) >> 8;".
That was pretty much my first post here lol
I think you got the calculation wrong though, because doing it that way means you can never get 255 (don't forget that shifting rounds down). It should be out_blue = ((blue1 * alpha) + (blue2 * ~alpha) + 255) >> 8. That +255 causes the entire range to be skewed so the extreme values always give the correct result after the shift. Also don't forget to make sure that ~ applies as a byte-sized value (hint: C will cast to int then apply ~ which won't be nice, so you better account for that).
And yeah it's not correct but if performance is an issue then better to have it look "somewhat" OK. Probably not worth the effort being correct until you can use the GPU to crunch numbers, making the UI feel more responsive to the user is more important than cosmetic details.
EDIT: right, I just remembered the other possible way to fix the calculation
out_blue = ((blue1 * (alpha + 1)) + (blue2 * ~alpha)) >> 8
That +1 may be more useful since it could end up compiled into an INC (also since you'd be using alpha+1 and ~alpha three times each, they're likely going to be computed once and stored into their own variables then their result reused).
Re: alpha blending
Posted: Thu Jan 05, 2017 8:22 pm
by szhou42
Does SSE works on emulators like QEMU and BOCHS ? I tried them before and didn't see much improvement.
Re: alpha blending
Posted: Fri Jan 06, 2017 4:44 am
by matt11235
szhou42 wrote:Does SSE works on emulators like QEMU and BOCHS ? I tried them before and didn't see much improvement.
I think you can enable SSE and other extensions with the -cpu flag in QEMU. I'm unsure about Bochs though.