PNGs with transparency in SDL.

Started by
5 comments, last by brakhage 10 years, 10 months ago
I'm drawing some PNGs with transparency in SDL (using SDL_BlitSurface(), which works just fine), but I'm having problems with changing the alpha values (to fade the image in/out).

I've tried doing something like this for all the pixels:

*pixel = SDL_MapRGBA(image->format, r, g, b, a);

Changing the value for "a" in the call to SDL_MapRGBA() works as expected on PNG files without transparency, but if the image has transparency, I instead end up with this:

lolwut.jpg

Here, "a" was set to 255. Changing the alpha still works (changes the opacity), but it's like all the pixels have been replaced by black ones, except for the 40 lines of pixels at the top, which are white.

Any idea what's going on?
Advertisement
Maybe you should posts some more code.
Alright.

I set the video mode like this:
screen = SDL_SetVideoMode(800, 600, 32, SDL_HWSURFACE | SDL_DOUBLEBUF);

Then I load an image using SDL_image:
SDL_Surface* temp  = IMG_Load(file.c_str());
SDL_Surface* image = SDL_DisplayFormatAlpha(temp);

Then I draw it to the screen:
SDL_BlitSurface(source, NULL, destination, NULL);

This works fine, transparency and everything. If I load a .png without transparency (or a .bmp or whatever) I can play around with the alpha bits and it works fine, but like I said, if I try that with a .png with transparency, everything gets screwed up.
I could start throwing guesses at you but it would be easier if you posted the whole code if it's not too long.
#include <sdl.h>
#include <sdl_image.h>

void applySurface(SDL_Surface* source, SDL_Surface* destination, int x, int y)
{
    SDL_Rect offset;

    offset.x = x;
    offset.y = y;

    SDL_BlitSurface(source, NULL, destination, &offset);
}

SDL_Surface* loadImage(std::string file)
{
    SDL_Surface* temp = IMG_Load(file.c_str());
    SDL_Surface* image = SDL_DisplayFormatAlpha(temp);
    SDL_FreeSurface(temp);

    return(image);
}

int main(int argc, char* argv[])
{
    SDL_Init(SDL_INIT_EVERYTHING);

    SDL_Surface* screen(nullptr);
    screen = SDL_SetVideoMode(800, 600, 32, SDL_HWSURFACE | SDL_DOUBLEBUF);

    SDL_ShowCursor(SDL_DISABLE);

    SDL_Surface* bg = loadImage("data/bg.png");
    SDL_Surface* img1 = loadImage("data/img1.png");

    bool done(false);
    SDL_Event event;

    while(!done)
    {
        while(SDL_PollEvent(&event))
        {
            if(event.type == SDL_QUIT || (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_ESCAPE))
            {
                done = true;
            }
        }

        applySurface(bg, screen, 0, 0);

        static Uint8 alpha(0);

        for(int y(0); y < img1->h; ++y)
        {
            for(int x(0); x < img1->w; ++x)
            {
                Uint32* pixel = (Uint32*)img1->pixels + (x + y * img1->w);

                Uint8 r;
                Uint8 g;
                Uint8 b;
                Uint8 a;

                SDL_GetRGBA(*pixel, img1->format, &r, &g, &b, &a);
                *pixel = SDL_MapRGBA(screen->format, r, g, b, alpha);
            }
        }

        ++alpha;

        applySurface(img1, screen, 0, 0);

        SDL_Flip(screen);
    }

    SDL_Quit();

    return(0);
}

I've tried several different ways of changing the alpha value, but all with the same result.
You are passing the wrong format to SDL_MapRGBA. screen->format should be img1->format.

When you set the alpha value for each pixel you are ignoring the alpha values that the image was saved as so you will see pixels that are not supposed to be seen.

To not destroy the original alpha values you could draw to the screen surface directly. If you change alpha to a floating point type and slowly change it from 0 to 1 and do something like this inside the loop
*screenPixel = SDL_MapRGBA(screen->format, r, g, b, a * alpha);
I think you will get the effect you want.

Another way I thought would work was to set the per-surface alpha value using SDL_SetAlpha but that doesn't appear to work.

To not destroy the original alpha values you could draw to the screen surface directly. If you change alpha to a floating point type and slowly change it from 0 to 1 and do something like this inside the loop


*screenPixel = SDL_MapRGBA(screen->format, r, g, b, a * alpha);
I think you will get the effect you want.


Yes, that works. Thank you. smile.png

This topic is closed to new replies.

Advertisement