Blitting RGBA surfaces to RGBA surfaces

Hi,

when blitting something from a RGBA surface to another RGBA surface,
somehow the Alpha channel isn’t transferred. Therefore, if I blit onto a
newly created surface, I still don’t see anything. Is there any
convenient way around this?

What I’m trying to do is to write a text rendering system the following way:

  • I have a character map which contains all characters I need
  • I have a font definition file which contains the coordinates of the
    characters within the map
  1. I (obviously) load the character map (which is in a PNG with alpha
    channel) into an SDL_Surface this way:

SDL_Surface* surface1 = IMG_Load(filename);
SDL_Surface* fontImage = SDL_DisplayFormatAlpha(surface1);
SDL_FreeSurface(surface1);

When I blit this to the screen, it looks perfect, with correct
transparencies and everything.

  1. I have the function renderText():

SDL_Surface* surface1 = SDL_CreateRGBSurface(SDL_SRCALPHA, width, height, 32,
0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF);
SDL_Surface* target = SDL_DisplayFormatAlpha(surface1);
SDL_FreeSurface(surface1);

//For every character…
SDL_BlitSurface(fontImage, &srcrct, target, &dstrct);

return target;

Any help would be appreciated.

Sebastian

Sebastian Beschke wrote:

when blitting something from a RGBA surface to another RGBA surface,
somehow the Alpha channel isn’t transferred. Therefore, if I blit onto a
newly created surface, I still don’t see anything. Is there any
convenient way around this?

in pygame we had this same problem. there are some easily
extractable ‘alpha to alpha’ blitting functions you should
be able to use (they are LGPL, like SDL).

currently they are a bit “hacky”, and i’d love if someone
more mathematically knowledgeable could make the “correct”.

if the destination alpha is 0, then the source alpha is
copied. otherwise the source and destination alphas are
properly blended.

the problem with always blending, if the dest has 0 alpha,
it ends up darkening the pixels blended onto it. is there a
compositing method that factors in the destination alpha to
the amount of destination color to mix? i tried a few things
and was not pleased with the results.

anyways, for assembling several alpha text images onto a
bigger surface, this works great (and mainly what our users
are taking advantage of it for).

http://cvs.seul.org/cgi-bin/cvsweb-1.80.cgi/games/pygame/src/alphablit.c?rev=1.3&content-type=text/x-cvsweb-markup
(beware the long url wrapping)

just call the “pygame_AlphaBlit” instead of “SDL_Blit” and
you’re all set.

This probably isn’t the advice you’re looking for, by try SFont. It does the
same thing you’re trying to do, and can be found on SDL’s website, in the
libraries section. At the very least it could give you some ideas as to why
your code isn’t working.

  1. I have the function renderText():

SDL_Surface* surface1 = SDL_CreateRGBSurface(SDL_SRCALPHA, width, height,
32, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF);
SDL_Surface* target = SDL_DisplayFormatAlpha(surface1);
SDL_FreeSurface(surface1);

//For every character…
SDL_BlitSurface(fontImage, &srcrct, target, &dstrct);

return target;

Creating a surface and then changing its format every time renderText() is
called is going to be slow. IMHO a better idea would be just to pass it the
target surface as an argument and leave it up to the calling function to make
sure that the font surface and the target surface are in the same format.

-Sean Ridenour

in pygame we had this same problem. there are some easily
extractable ‘alpha to alpha’ blitting functions you should
be able to use (they are LGPL, like SDL).

currently they are a bit “hacky”, and i’d love if someone
more mathematically knowledgeable could make the “correct”.

if the destination alpha is 0, then the source alpha is
copied. otherwise the source and destination alphas are
properly blended.

What you’re doing sounds correct. What happens in professional compositing
programs when using the standard over operation is this:
1. If the foreground image is premultiplied, go to step 3
2. Multiply the foreground image against its own alpha channel
3. Invert the foreground image’s alpha channel
4. Multiply the background image (all channels) by the foreground
image’s newly inverted alpha channel.
5. Revert the foreground image’s alpha channel to its original state
6. Add the foreground image to the background image (all channels)

Now, if the destination alpha is 0, then just copying the source alpha would
accomplish the same thing as all the steps above, at least as far as the
alpha channel is concerned. So you are correct in that optimization.

Whether or not this is what SDL does is another matter. As far as blitting a
surface with an alpha channel to another surface with an alpha channel, and
then blitting that surface to the screen goes, it seems to work OK for me.
On my system (Linux, SDL 1.2.5, GeForce2 MX), the following program works
exactly as expected (the image used, test.png, is available at
http://kewlpc.org/pics/test.png )

/* compile with
gcc sdltest.c -o sdltest sdl-config --cflags --libs -lSDL_image
*/

#include <stdio.h>
#include “SDL.h”
#include “SDL_image.h”

int main(void)
{
SDL_Surface *screen;
SDL_Surface *image;
SDL_Surface *target;
SDL_Event event;
int quit = 0;

SDL_Init(SDL_INIT_VIDEO);

atexit(SDL_Quit);

screen = SDL_SetVideoMode(640,480,32,SDL_DOUBLEBUF);

SDL_WM_SetCaption("SDL TEST",NULL);

image = IMG_Load("test.png");

SDL_SetAlpha(image,SDL_RLEACCEL|SDL_SRCALPHA,255);

target = SDL_DisplayFormatAlpha(image);

SDL_FillRect(target,NULL,SDL_MapRGBA(target->format,0,0,0,0));

SDL_BlitSurface(image,NULL,target,NULL);

SDL_FillRect(screen,NULL,SDL_MapRGB(screen->format,0,128,128));

SDL_BlitSurface(target,NULL,screen,NULL);

SDL_Flip(screen);

while(!quit) {
	while(SDL_PollEvent(&event)) {
		switch(event.type) {
		case SDL_MOUSEBUTTONDOWN:
			quit = 1;
			break;
		case SDL_QUIT:
			quit = 1;
			break;
		default:
			break;
		}
	}
}

return 0;

}

the problem with always blending, if the dest has 0 alpha,
it ends up darkening the pixels blended onto it. is there a
compositing method that factors in the destination alpha to
the amount of destination color to mix? i tried a few things
and was not pleased with the results.

Even if you are always blending, when blended properly this shouldn’t happen.
The destination alpha should have nothing to do with it. On my machine, using
the above code (if you’ll notice, target has an alpha of 0), what you’re
describing doesn’t happen.

-Sean Ridenour

Hi,
thanks for the info!

Your example program works on my machine, too.

Somehow the SDL_SetAlpha() seems to do the trick, because when I comment
it out, I don’t see the image anymore. Funny thing is:

image = IMG_Load("test.png");
image2 = SDL_DisplayFormatAlpha(image);
SDL_SetAlpha(image,SDL_RLEACCEL|SDL_SRCALPHA,255);
target = SDL_DisplayFormatAlpha(image2);

In this code, the SDL_SetAlpha is still vital for the image being
displayed… does it have any side effects? (I don’t even use the image
surface later in the program!)

Well whatever, I think I’m switching to SFont - thanks for the hint!

Thanks again, also to Pete Shinners :slight_smile:

In this code, the SDL_SetAlpha is still vital for the image being
displayed… does it have any side effects? (I don’t even use the image
surface later in the program!)

It has no side effects that I’m aware of. But it’s a good idea to call
SDL_SetAlpha() anyway, simply because you can set the SDL_RLEACCEL flag,
which makes blitting of alpha blended images much faster. Basically, instead
of doing the alpha blending for each and every pixel, SDL will first look at
the source surface’s alpha channel. If it’s 0 at the pixel we’re currently
at, then SDL just skips it and goes on to the next. If it’s 255, then SDL
just copies the source surface’s pixel over the destination’s pixel without
doing any alpha blending (since it would be opaque anyway).

-Sean Ridenour