How does one efficiently implement alpha blending, without botching gamma?
Alpha blending is basically the following expression:
result_color = (dst_color*src_alpha - dst_color*src_alpha*dst_alpha + src_color - src_color*src_alpha)/(1 - src_alpha*dst_alpha)
result_alpha = src_alpha*dst_alpha
The formula is unusual because I use inverted alpha, where 0 means opaque and 1 - completely transparent. Rationale: many CPUs have instruction to compare with zero, but no instruction to compare with 0xff000000. Also, I can pass just 0xRRGGBB to set_pixel instead of 0xffRRGGBB for opaque color.
For gamma-packed 8-bit RGB I've managed to implement that expression somewhat efficiently in early 90ies style code:
void ablend(int dr,int dg,int db,int da, int *sr,int *sg,int *sb,int *sa) {
int ya = (da * *sa)>>8;
uint32_t d = idiv_lut[255 - ya];
uint8_t *st = ab_lut[255 - *sa];
uint8_t *dt = ab_lut[*sa - ya];
*sr = ((dt[dr] + st[*sr])*d)>>8;
*sg = ((dt[dg] + st[*sg])*d)>>8;
*sb = ((dt[db] + st[*sb])*d)>>8;
*sa = ya;
}
Obviously working with RGB, without unpacking it first, produces incorrect result (it is the most common graphics programming pitfall), so they must be unpacked (i.e. pow(x,2.2)), and now one can't use ab_lut anymore, because it would require 2^30 bytes, and replacing division with multiplication would be impossible on a 32-bit system either.
Give that, this question implies some side questions:
Is using float the only solution for unpacked RGB processing?
How can I perform alpha blending with gamma correction?
Is it worth it (L1 cache-wise) to still use gamma packed sRGB, instead of a simple array of 4 32-bit float R,G,B,A components, or would memory access become a bottleneck on a majority of CPUs?
So maybe I just have this 90's mindset, where keeping data size small meant difference between 1 and 60 frames per second, and today we can safely use as many additional bytes as required?