Question

I'm trying to blit a surface created by IMG_Load (SDL_image) onto one created via SDL_CreateRGBSurface. When both surfaces are loaded with IMG_Load, this works fine, but not when the target surface was created using SDL_CreateRGBSurface.

By experimentation, I figured out that if I call SDL_SetAlpha on the source surface, it suddenly works fine. The documentation is a bit lacking, but how I understand it, the way I call it below should clear the SDL_SRCALPHA flag, presumably set by IMG_Load. It seems like the alpha flag of the source surface is what matters when blitting, so I guess this turns off alpha blending altogether.

The big question is: Why is this SDL_SetAlpha necessary in the first place? And is this really the right way to do it?

Here's code that reproduces this:

#include <SDL/SDL.h>
#include <SDL_image/SDL_image.h>

SDL_Surface* copy_surface(SDL_Surface* source)
{
    SDL_Surface *target;

    target = SDL_CreateRGBSurface(0, source->w, source->h,
                                  source->format->BitsPerPixel,
                                  source->format->Rmask, source->format->Gmask,
                                  source->format->Bmask, source->format->Amask);

    /*
     * I really don't understand why this is necessary. This is supposed to
     * clear the SDL_SRCALPHA flag presumably set by IMG_Load. But why wouldn't
     * blitting work otherwise?
     */
    SDL_SetAlpha(source, 0, 0);

    SDL_BlitSurface(source, 0, target, 0);
    return target;
}

int main(int argc, char* argv[])
{
    SDL_Event event;
    SDL_Surface *copy, *screen, *source;

    SDL_Init(SDL_INIT_VIDEO);
    screen = SDL_SetVideoMode(800, 600, 0, 0);
    SDL_FillRect(screen, 0, SDL_MapRGB(screen->format, 0xff, 0xff, 0xff));
    source = IMG_Load("source.png");
    if (!source) {
        fprintf(stderr, "Unable to load source image\n");
        return 1;
    }
    copy = copy_surface(source);
    SDL_BlitSurface(copy, 0, screen, 0);
    SDL_Flip(screen);
    while (SDL_WaitEvent(&event))
        if (event.type == SDL_QUIT
            || (event.type == SDL_KEYUP && event.key.keysym.sym == SDLK_ESCAPE))
            break;
    SDL_Quit();
    return 0;
}

You'll need source.png. I'm using a 400x400 transparent PNG with some black scribbling.

Was it helpful?

Solution

http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlsetalpha.html describes blitting for each possible pixel format combination.

When alpha blending is performed, standard formula is SRC_COLOUR * SRC_ALPHA + DST_COLOUR * (1 - SRC_ALPHA). Destination alpha isn't used in here.

If alpha blending is enabled, and SRCALPHA flag is enabled for source surface, source alpha is used to re-calculate (blend) updated destination colours, but alpha channel of destination is left untouched. Next time this 'dst' used as source of blitting, its alpha would be used.

To avoid your problem, there are several possible ways:

  1. Disable SRCALPHA for source surface (you already tried that)
  2. Disable SRCALPHA for destination surface, so it will not be blended with screen. It does not affect initial blit, but does affect next one, when your 'target' (or 'copy') used as source to blit on screen.
  3. Fill destination surface with opaque white. It does, however, affect blitting, on [partially]-transparent parts of source (alpha blending with white and with black produces different results).
  4. Manually copy pixels. If pixel format and surface dimensions match, mere memcpy would be enough. This will overwrite contents of destination, not blend it. It also would be noticeably faster than blend. If dimensions do not match - depending on pitch parameter you probably would need number_of_rows memcpy's. If pixel formats do not match - copying is problematic, you need to decode each pixel from source pixel format and encode it to destination pixel format.
  5. Use SDL_ConvertSurface (e.g. SDL_ConvertSurface(source, source->format, 0). It will create new surface with the same contents. I think this way is preferred. Don't forget to free new surface later.
  6. Use SDL_DisplayFormatAlpha if you use SDL 1.2. This is almost the same as SDL_ConvertSurface, but converts surface to display format, not source surface's one. It is no longer available in SDL2.

Also note that (4), (5) and (6) are only ways I see that allows you to keep source alpha. All blitting operations discarding source alpha, leaving destination with its own alpha values - as blitting never updates destination alpha.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top