Sign in to follow this  

Problem with Transparency and SDL

This topic is 3295 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello, I have been working a program in SDL and ran into some problems making surfaces with a transparent background. I have slimmed down and isolated the code as much as I could, but since I don't know why I am unable to get the desired results, its still a little long. If anyone has a spare moment, please look at my code, I'm quite addled. =] Problem: I create a surface with SDL_CreateRGBSurface, then blit a png (loaded with SDL_image) onto it. I then blit the created surface to the screen. Nothing appears. The only way I can get an image the screen, is if I first call SDL_FillRect on the created surface. This thus defeats my goal, as the created surface no longer has a transparent background (colour keying failed). I don't know how to attach a file, but the png is 24x24 in size, 32bpp. It has an alpha channel.
#include <iostream>
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>

using namespace std;

SDL_Surface *loadImage(const std::string& fileName){
    SDL_Surface *temp1 = NULL;
    SDL_Surface *temp2 = NULL;
    temp1 = IMG_Load(fileName.c_str());
    if(temp1 != NULL){
        if(temp1->flags & SDL_SRCALPHA){
            temp2 = SDL_DisplayFormatAlpha(temp1);
        } else {
           temp2 = SDL_DisplayFormat(temp1);
        }
        if( temp2 == NULL ){
            cerr << __FILE__ << " " << __LINE__ << ": " << "SDL_DisplayFormat \"" << fileName << "\": " << IMG_GetError() << endl;
            return temp1;
        } else {
            SDL_FreeSurface(temp1);
            return temp2;
        }
    } else{
        cerr << __FILE__ << " " << __LINE__ << ": " << "Unable to load file \"" << fileName << "\": " << IMG_GetError() << endl;
        SDL_FreeSurface(temp1);
        return NULL;
    }
}

void drawSurface(SDL_Surface *src, SDL_Surface *destin, int x, int y){
    SDL_Rect dest;
    dest.x = x;
    dest.y = y;
    if(SDL_BlitSurface(src, NULL, destin, &dest) != 0){
        cerr << __FILE__ << " " << __LINE__ << ": " << "Blitting " << src << " to " << destin << " at (" << x << ", " << y << ") failed: " << SDL_GetError() << endl;
    }
}

SDL_Surface *createSurface(int width, int height, int depth, Uint32 flags){
    if( depth == 32 ){ flags |= SDL_SRCALPHA; }
    flags |= SDL_RLEACCEL;
    Uint32 rmask, gmask, bmask, amask;

#if SDL_BYTEORDER == SDL_BIG_ENDIAN
    rmask = 0xff000000;
    gmask = 0x00ff0000;
    bmask = 0x0000ff00;
    amask = 0x000000ff;
#else
    rmask = 0x000000ff;
    gmask = 0x0000ff00;
    bmask = 0x00ff0000;
    amask = 0xff000000;
#endif
    SDL_Surface *temp1 = SDL_CreateRGBSurface(  flags, width, height, depth,
                                                rmask, gmask, bmask,  amask   );
    SDL_Surface *temp2;
    if(temp1 != NULL){
        if(temp1->flags & SDL_SRCALPHA){
            temp2 = SDL_DisplayFormatAlpha(temp1);
        } else {
            temp2 = SDL_DisplayFormat(temp1);
        }
        if( temp2 == NULL ){
            cerr << __FILE__ << " " << __LINE__ << ": " << "Unable to create surface: " << SDL_GetError() << endl;
            return temp1;
        } else {
            SDL_FreeSurface(temp1);
            return temp2;
        }
        return temp2;
    } else{
        cerr << __FILE__ << " " << __LINE__ << ": " << "Unable to create surface" << ": " << SDL_GetError() << endl;
        return NULL;
    }
}

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

    SDL_Surface *screen = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE|SDL_ANYFORMAT);
    SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0,255,0));
    SDL_Surface *bubbleSource = loadImage("speechBubble.png");
    SDL_Surface *fusedImage = createSurface(200, 200, 32, SDL_HWSURFACE|SDL_SRCALPHA);

    //if i dont uncommented this, i get nothing but a black screen.
    //SDL_FillRect(fusedImage, NULL, SDL_MapRGB(fusedImage->format, 255, 0, 255));

    drawSurface(bubbleSource, fusedImage, 0, 0);
    drawSurface(bubbleSource, fusedImage, 100, 0);

    drawSurface(fusedImage, screen, 0, 0);

    SDL_Flip(screen);
    SDL_Delay(2000);

    SDL_FreeSurface(bubbleSource);
    SDL_FreeSurface(fusedImage);

    SDL_Quit();
    return 0;
}


I don't know why, but when I paste code, the formatting disappears. -Thank you in advance ----------------------------------------- EDIT: I fixed the formatting =] [Edited by - insanepotato on December 5, 2008 11:58:30 PM]

Share this post


Link to post
Share on other sites
I am too tired right now to actually go and try to help you (and it has been forever sense I have used SDL), but here it is for the other people to have an easier time reading it.


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

using namespace std;

SDL_Surface *loadImage(const std::string& fileName){
SDL_Surface *temp1 = NULL;
SDL_Surface *temp2 = NULL;
temp1 = IMG_Load(fileName.c_str());
if(temp1 != NULL){
if(temp1->flags & SDL_SRCALPHA){
temp2 = SDL_DisplayFormatAlpha(temp1);
} else {
temp2 = SDL_DisplayFormat(temp1);
}
if( temp2 == NULL ){
cerr << __FILE__ << " " << __LINE__ << ": " << "SDL_DisplayFormat \"" << fileName << "\": " << IMG_GetError() << endl;
return temp1;
} else {
SDL_FreeSurface(temp1);
return temp2;
}
} else{
cerr << __FILE__ << " " << __LINE__ << ": " << "Unable to load file \"" << fileName << "\": " << IMG_GetError() << endl;
SDL_FreeSurface(temp1);
return NULL;
}
}

void drawSurface(SDL_Surface *src, SDL_Surface *destin, int x, int y){
SDL_Rect dest;
dest.x = x;
dest.y = y;
if(SDL_BlitSurface(src, NULL, destin, &dest) != 0){
cerr << __FILE__ << " " << __LINE__ << ": " << "Blitting " << src << " to " << destin << " at (" << x << ", " << y << ") failed: " << SDL_GetError() << endl;
}
}

SDL_Surface *createSurface(int width, int height, int depth, Uint32 flags){
if( depth == 32 ){ flags |= SDL_SRCALPHA; }
flags |= SDL_RLEACCEL;
Uint32 rmask, gmask, bmask, amask;

#if SDL_BYTEORDER == SDL_BIG_ENDIAN
rmask = 0xff000000;
gmask = 0x00ff0000;
bmask = 0x0000ff00;
amask = 0x000000ff;
#else
rmask = 0x000000ff;
gmask = 0x0000ff00;
bmask = 0x00ff0000;
amask = 0xff000000;
#endif
SDL_Surface *temp1 = SDL_CreateRGBSurface( flags, width, height, depth,
rmask, gmask, bmask, amask );
SDL_Surface *temp2;
if(temp1 != NULL){
if(temp1->flags & SDL_SRCALPHA){
temp2 = SDL_DisplayFormatAlpha(temp1);
} else {
temp2 = SDL_DisplayFormat(temp1);
}
if( temp2 == NULL ){
cerr << __FILE__ << " " << __LINE__ << ": " << "Unable to create surface: " << SDL_GetError() << endl;
return temp1;
} else {
SDL_FreeSurface(temp1);
return temp2;
}
return temp2;
} else{
cerr << __FILE__ << " " << __LINE__ << ": " << "Unable to create surface" << ": " << SDL_GetError() << endl;
return NULL;
}
}

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

SDL_Surface *screen = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE|SDL_ANYFORMAT);
SDL_Surface *bubbleSource = loadImage("speechBubble.png");
SDL_Surface *fusedImage = createSurface(200, 200, 32, SDL_HWSURFACE|SDL_SRCALPHA);

//if i dont uncommented this, i get nothing but a black screen.
SDL_FillRect(fusedImage, NULL, SDL_MapRGB(fusedImage->format, 255, 0, 255));

drawSurface(bubbleSource, fusedImage, 0, 0);
drawSurface(bubbleSource, fusedImage, 100, 0);

drawSurface(fusedImage, screen, 0, 0);

SDL_Flip(screen);
SDL_Delay(2000);

SDL_FreeSurface(bubbleSource);
SDL_FreeSurface(fusedImage);

SDL_Quit();
return 0;
}



all you do is this:

[ source]
code here
[ /source]

Just delete the spaces

Share this post


Link to post
Share on other sites
Looks like you never fill in the fused image with anything. I suspect this gives it pixel values of 0,0,0,0: ie. black, with 0% alpha. If you blit to that, the black will change, but the alpha will stay at 0, so when you finally blit that image to the screen, you see nothing. Just an idea.

Another suggestion for you; clear your screen to green or grey or something each frame. That way, if you do accidentally blit an entirely black image, which is a symptom of several problems you can get with this sort of thing, at least you'll see something happening.

Share this post


Link to post
Share on other sites
Quote:
Original post by Kylotan
Looks like you never fill in the fused image with anything. I suspect this gives it pixel values of 0,0,0,0: ie. black, with 0% alpha. If you blit to that, the black will change, but the alpha will stay at 0, so when you finally blit that image to the screen, you see nothing. Just an idea.

Another suggestion for you; clear your screen to green or grey or something each frame. That way, if you do accidentally blit an entirely black image, which is a symptom of several problems you can get with this sort of thing, at least you'll see something happening.



I think your right about the pixel values of (0,0,0,0), because when I fill it with SDL_MapRGBA using something like (255,0,255,128), my final fused image is drawn to the screen half transparent.

But it doesn't make sense to me, since my source image has an alpha channel, shouldn't the source's alpha values be used for the pixels it occupies on the fused image after the blit? So that the areas with nothing blited on them will still be reamain (0,0,0,0)

Thank you for the replies so far, I really appreciate it =)

Share this post


Link to post
Share on other sites
To simplify this a bit have you tried using color keys instead of alpha?

I honestly haven't read through your code very thoroughly because the formatting hurts my eyes (copy paste gone wrong) however I wanted to save you your next post which will most likely be something about SDL being slow.

As many previous posts have shown - using an alpha and SDL blitting can be very slow - which is why I suggest color keying.

With color keying you would basically change your code to load the image to the following:


SDL_Surface *temp = IMG_Load( filename_c_str() );
SDL_SetColorKey( temp, SDL_SRCCOLORKEY|SDL_RLEACCEL, SDL_MapRGB( temp->format, red,green,blue ) );
SDL_Surface *final = SDL_DisplayFormat(temp);
SDL_FreeSurface(temp);



The red, green, blue variables represent the RGB color in the image you want not to be drawn. Also, I recommend using a 24 bit bitmap since disk space is cheap and the image gets uncompressed in your game when SDL loads it anyway so using a compressed image format really doesn't save you much anymore unless you are distributing your game as a download and it is greatly increasing your download size. This also allows you to not require SDL_image (i.e. less overhead and less dll hell on windows anyway).

If you have used color keying and found it doesn't meet your requirements you will definitely want to look into using OpenGL or some other 3d rendering API as these will happily use your alpha channel and be very fast with it as well. The down side being that 3d APIs have a much higher learning curve than 2d even when using them for 2d.

Share this post


Link to post
Share on other sites
I initially chose SDL_image as to 'keep as many doors open as possible'.

I decided to use an alpha channel for convenience, despite the potential slow down. I would really appreciate a solution or an explanation as to why my code is 'misbehaving', although, if no solution is to be found, I guess I'll exchange the alpha channel for Magic Pink =]

Sorry about the formatting.

[Edited by - insanepotato on December 8, 2008 9:02:17 PM]

Share this post


Link to post
Share on other sites

This topic is 3295 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this