Blitting order

For sure that some of you have noticed that if you have 3 surfaces: A, B
and C, and you do the following:

1.- blit (blit C over B) A, i.e. blit C over B, and then blit the result
over A

or

2.- blit C over (blit B over A), i.e. blit B over A, and then blit C
over the result

You obtain different results if you have some alpha values different
than 0 or 255.
However, this is a problem sometimes, since if B and C are fixed, you
are making 2 blits every time you redraw your screen. To solve this
problem, I’ve made a function that blits a surface over another that
acomplished the asociative property, i.e. “blit (blit C over B) A” is
equal to “blit C over (blit B over A)”. So, you can blit C over B, and
at each frame just blit this result over A. Resulting in a single blit
per frame.

I don’t know if some one is interested in such a function. But it’s been
very usefull to me.
I include the source code here (It’s completely unoptimized, but it does
the work). Notice that I’ve divided by 255 instead of 256 to normalize
the alpha values. But this is because the alpha values are between 0 and
255 and not between 0 and 256. I just wanted EXACT calculations.

void surface_blit_rightalpha(SDL_Surface *orig,SDL_Rect *or,SDL_Surface
*dest,SDL_Rect *dr)
{
int i,j;
SDL_Rect r1,r2;
Uint8 *opixels,*dpixels;
int a1,a2;
int ap,app;

if (orig->format->BytesPerPixel!=4 ||
    dest->format->BytesPerPixel!=4) return;

if (or!=0) {
    r1=*or;
} else {
    r1.x=0;
    r1.y=0;
    r1.w=orig->w;
    r1.h=orig->h;
} /* if */

if (dr!=0) {
    r2=*dr;
} else {
    r2.x=0;
    r2.y=0;
} /* if */

if (r1.x<0) {
    r1.w+=r1.x;
    r1.x=0;
} /* if */
if (r1.x>=dest->w) return;
if (r1.y<0) {
    r1.h+=r1.y;
    r1.y=0;
} /* if */
if (r1.y>=dest->h) return;

if (r2.x<0) {
    r1.w+=r2.x;
    r2.x=0;
} /* if */
if (r2.x>=dest->w) return;
if (r2.y<0) {
    r1.h+=r2.y;
    r2.y=0;
} /* if */
if (r2.y>=dest->h) return;
if (r1.w<0) return;
if (r2.x+r1.w>dest->w) r1.w=dest->w-r2.x;
if (r1.h<0) return;
if (r2.y+r1.h>dest->h) r1.h=dest->h-r2.y;

SDL_LockSurface(orig);
SDL_LockSurface(dest);

for(i=0;i<r1.h;i++) {
    opixels=(Uint8 *)orig->pixels+orig->pitch*(i+r1.y)+r1.x*4;
    dpixels=(Uint8 *)dest->pixels+dest->pitch*(i+r2.y)+r2.x*4;
    for(j=0;j<r1.w;j++,opixels+=4,dpixels+=4) {
        a2=opixels[AOFFSET];
        if (a2!=0) {
            a1=dpixels[AOFFSET];

            ap=a1+a2-(a1*a2)/255;
            if (ap>255) ap=255;
            if (ap<0) ap=0;

            if (a2==0) app=0;
                  else app=(-a2*255)/(((a1*a2)/255)-a1-a2);
            if (app>255) app=255;
            if (app<0) app=0;

dpixels[ROFFSET]=(opixels[ROFFSET]app+dpixels[ROFFSET](255-app))/255;

dpixels[GOFFSET]=(opixels[GOFFSET]app+dpixels[GOFFSET](255-app))/255;

dpixels[BOFFSET]=(opixels[BOFFSET]app+dpixels[BOFFSET](255-app))/255;
dpixels[AOFFSET]=ap;
} /* if /
} /
for /
} /
for */

SDL_UnlockSurface(orig);
SDL_UnlockSurface(dest);

} /* surface_blit_rightalpha */