Alpha Blending
Alpha blending is the term used to call the mixing of two bitmaps using a parameter called alpha which determinates the proportionality of intensity between each bitmap in the blending process.
Alpha blending is used in cross fades, transparencies, particles, etc.
Sometimes is useful (when speed is a very important issue) to use a simpler approach of alpha blending called satured adds blending, and it is practically the same, just that you don't use an alpha parameter.
Satured Adds
A satured add is an add with a special property, and that is that when you add two values that should overflow, instead of overflowing you just get the largest value that that data type can content. For example, if you are doing a saturated add of bytes, and you have:
b1 = 150
b2 = 200
res = b1 +b2;
Using normal addition: res = 95
Using satured additions: res = 255
It is very easy to simulate satured adds with normal Intel 80x86 instructions. The registers al and dl hold the values to add; result will be stored in al.
add al,dl
sbb cl,cl
or al,cl
To perform blending in this way, you just saturate add the R,G,B components of each pixel.
add al,R
sbb ecx,ecx
add ah,G
sbb ch,ch
add dl,B
sbb cl,cl
shl edx,16
or eax,edx
or eax,ecx
Saturated adds give a very bad image quality blending bitmaps, but they are the way to go when implementing particles. You just have a particle sprite, and you put that sprite on the screen buffer using a saturated add tecnique. They are also useful in transparent polygon fillers, since alpha blending would be too slow in this case.
Of course saturated adds are quite CPU expensive, so fortunately we have MMX. In MMX you have a special instruction to perform saturated Adds, plus you can add up to 8 bytes at the same time with one instruction.
Alpha Blending
Alpha blending gives a much better result, and it is possible to use it for creating cross fades.
The formula for this is:
newcolor = dest*(1-alpha) + origin*alpha
In a first approach we could write the following code (unoptimized for clarity reasons):
// RGBcol1 and RGBcol2 are our 32bpp pixel
unsigned char R1,G1,B1,R2,G2,B2;
unsigned long Rout,Gout,Bout,RGBout;
R1 = (unsigned char) RGBcol1>>16;
G1 = (unsigned char) RGBcol1>>8;
B1 = (unsigned char) RGBcol1;
R2 = (unsigned char) RGBcol1>>16;
G2 = (unsigned char) RGBcol1>>8;
B2 = (unsigned char) RGBcol1;
Rout = ((R1*(1-alpha) + R2*(alpha))<<16;
Gout = ((G1*(1-alpha) + G2*(alpha))<<8;
Bout = (B1*(1-alpha) + B2*(alpha);
RGBout = Rout + Gout + Bout;
// RGBout has the resulting alphablended pixel
This first approach works but it's not very fast. It's possible to modify it a bit, and use integer math and operate two values at the same time:
// RGB1 and RGB2are the 32bpp pixels to blend
RGBout = (((( RGB1 & 0x00FF00FF)*alpha)>>8) & 0x00FF00FF)+
((((RGB1>>8) & 0x00FF00FF)*alpha) & 0xFF00FF00) +
((((RGB2 & 0x00FF00FF)*(256-alpha))>>8) & 0x00FF00FF) +
((((RGB2>>8) & 0x00FF00FF)*(256-alpha)) & 0xFF00FF00);
Of course using the MMX technology and the SIMD instructions you can make all those operations once per pixel.
To do a cross fade, for example, you just alphablend the two images going from alpha = 0 to alpha = 255.