SDL_Image/OGL: How to Load PNG with alpha

Started by
5 comments, last by Alpha Nox 16 years, 9 months ago
Hello, I've tried to work with glpng but it gave lots of weird errors when compiling so I decided to use SDL_Image to load PNGs with alpha channels for my textures but I can't seem to load and display them with the alpha channel, how do I do that? Thanks in advance, Victor Freire
Advertisement
By alpha channel, I assume you mean display them with transparency? Its been a while for me and SDL but this might help...

//alphaVar is an int - 0 is transparent, 255 is opaque.SDL_SetAlpha(some_sdl_surface, SDL_SRCALPHA|SDL_RLEACCEL, alphaVar);


Here is a choppy fade function I came across that -demonstrates- alpha transparency...
//  Fading with alphablending by Patrick Koomanvoid fade(SDL_Surface* p_surf_screen, SDL_Surface* p_surf_img, Uint32 ui_seconds, int b_fade_out) {  /* Becomes the black surface */  SDL_Surface* p_surf_black = NULL ;  /* Used when the Screen Surface equals the Image Surface */  SDL_Surface* p_surf_screen_copy = NULL ;  /* Used to calculate the steps to make a fade in the given time: */  Uint32 ui_old_time, ui_curr_time, ui_time_ms ;  float f_alpha ;  /* Becomes flag to pass when creating a Surface */  Uint32 ui_flags = SDL_SRCALPHA ;  /* Create the black surface in the format of the given screen */  if ((p_surf_screen->flags & SDL_HWSURFACE)) {    ui_flags |= SDL_HWSURFACE ;  }  else {    ui_flags |= SDL_SWSURFACE ;  }  if ((p_surf_black = SDL_CreateRGBSurface (ui_flags,    p_surf_screen->w, p_surf_screen->h,     p_surf_screen->format->BitsPerPixel,     p_surf_screen->format->Rmask, p_surf_screen->format->Gmask, p_surf_screen->format->Bmask,    p_surf_screen->format->Amask)) == NULL) {      /* Replace this line with you own error handling / logging */      fprintf (stderr, "fade: could not create the black Surface. (%s)\n", SDL_GetError ()) ;      return ;  }  /* Fill the Surface with black */  SDL_FillRect (p_surf_black, NULL, SDL_MapRGB (p_surf_screen->format, 0, 0, 0)) ;  /* Should we make a copy of the Screen? */  if (p_surf_screen == p_surf_img) {    if ((p_surf_screen_copy = SDL_CreateRGBSurface (ui_flags,    p_surf_screen->w, p_surf_screen->h,     p_surf_screen->format->BitsPerPixel,     p_surf_screen->format->Rmask, p_surf_screen->format->Gmask, p_surf_screen->format->Bmask,    p_surf_screen->format->Amask)) == NULL) {      /* Replace this line with you own error handling / logging */      fprintf (stderr, "fade: could not create a copy of the Screen Surface. (%s)\n", SDL_GetError ()) ;      SDL_FreeSurface (p_surf_black) ;      return ;    }    /* Ok, copy surfaces and set surfact pointer */    SDL_BlitSurface (p_surf_screen, NULL, p_surf_screen_copy, NULL) ;    p_surf_img = p_surf_screen_copy ;  }  /* Ok, we are now ready for the fade. These are the steps (looped):  1. Draw p_surf_img onto p_surf_screen, just an ordinary blit.  2. Decrease or increase (depends on fading in or out) the alpha value,    based on the elapsed time since the previous loop-iteration.  3. Draw p_surf_black onto p_surf_screen in the current alpha value.*/    ui_old_time = SDL_GetTicks () ;  ui_curr_time = ui_old_time ;  /* Convert the given time in seconds into miliseconds. */  ui_time_ms = ui_seconds * 1000 ;  if (b_fade_out) {    f_alpha = 0.0 ;    /* Loop until the alpha value exceeds 255 (this is the maximum alpha value) */    while (f_alpha < 255.0) {      /* Draw the image onto the screen */      SDL_BlitSurface (p_surf_img, NULL, p_surf_screen, NULL) ;      /* Draw the black surface onto the screen */      SDL_SetAlpha (p_surf_black, SDL_SRCALPHA, (Uint8) f_alpha) ;      SDL_BlitSurface (p_surf_black, NULL, p_surf_screen, NULL) ;      /* Update the timer variables */      ui_old_time = ui_curr_time ;      ui_curr_time = SDL_GetTicks () ;      /* Flip the screen Surface */      SDL_Flip (p_surf_screen) ;      /* Calculate the next alpha value */      f_alpha += 255 * ((float) (ui_curr_time - ui_old_time) / ui_time_ms) ;    }  }  else {    f_alpha = 255.0 ;    /* Loop until the alpha value exceeds 255 */    while (f_alpha > 0.0) {      /* Draw the image onto the screen */      SDL_BlitSurface (p_surf_img, NULL, p_surf_screen, NULL) ;      /* Draw the black surface onto the screen */      SDL_SetAlpha (p_surf_black, SDL_SRCALPHA, (Uint8) f_alpha) ;      SDL_BlitSurface (p_surf_black, NULL, p_surf_screen, NULL) ;      /* Update the timer variables */      ui_old_time = ui_curr_time ;      ui_curr_time = SDL_GetTicks () ;      /* Flip the screen Surface */      SDL_Flip (p_surf_screen) ;      /* Calculate the next alpha value */      f_alpha -= 255 * ((float) (ui_curr_time - ui_old_time) / ui_time_ms) ;    }  }  /* Free the black Surface */  SDL_FreeSurface (p_surf_black) ;  /* Free the Screen copy, if used */  if (p_surf_screen_copy != NULL) {    SDL_FreeSurface (p_surf_screen_copy) ;  }}


Sorry for the lack effort I put into this post, hope something is useful.
Quote:Original post by AaronA
By alpha channel, I assume you mean display them with transparency? Its been a while for me and SDL but this might help...

//alphaVar is an int - 0 is transparent, 255 is opaque.SDL_SetAlpha(some_sdl_surface, SDL_SRCALPHA|SDL_RLEACCEL, alphaVar);


Here is a choppy fade function I came across that -demonstrates- alpha transparency...
*** Source Snippet Removed ***

Sorry for the lack effort I put into this post, hope something is useful.



Don't worry, I appreciate all the help I can get!

That's not what I'm looking for though, I probably didnt explain it right, by alpha channel I mean displaying a sprite(in a texture) with something like colorkeying but not colorkeying, transparent pixels.

Thanks for spending your time answering my post anyway :)
i'm not sure how if there is support for it in SDL as i've never used it but i think you mean alpha blending, where each pixel has a seperate transparency value(the alpha value).

with this you can get translucency and other cool effects, if that is what you mean then check if sdl has support for it.

to implement it manually is a pain, SDL is quite popular so i assume there is some way to do alpha blending without all the manual legwork.



--------------------------------------EvilMonkeySoft Blog
In my code it seems like I create a new surface with an alpha channel, and then the image on to that. It's been a while since I wrote this code, but perhaps it might be of help for you:

Resource* ImageLoader::Load(const std::string &filename, unsigned int id, const LoaderInfo &info){	SDL_Surface *image;	SDL_Surface *texture;				int tex_width, tex_height;			        if((image = IMG_Load(filename.c_str())) == NULL)	{		lout << lwarning << filename << " could not be openeed." << lendl;		SDL_FreeSurface(image);		return 0;	} 			if(image->w <= 0 || image->h <= 0)	{		lout << lwarning << "Bad size of image " << filename << ". Image must be broken." << lendl;		SDL_FreeSurface(image);		return 0;	}				ImageResource *image_res = new ImageResource(id);	image_res->Width = image->w;	image_res->Height = image->h;			        //Make sure we create a texture with a size that is a power of 2	tex_width = image_res->Width;	if(!(tex_width & (tex_width - 1)))	{		image_res->TexWidth = 1.0f;	}	else	{		int i = 1;						while(i < tex_width)		{			i *= 2;			}							image_res->TexWidth = (float)tex_width / (float)i;		tex_width = i;	}				tex_height = image_res->Height;	if(!(tex_height & (tex_height - 1)))	{		image_res->TexHeight = 1.0f;	}	else	{		int i = 1;						while(i < tex_height)		{			i *= 2;			}							image_res->TexHeight = (float)tex_height / (float)i;		tex_height = i;	}		    	#if SDL_BYTEORDER == SDL_BIG_ENDIAN		Uint32 rmask = 0xff000000;		Uint32 gmask = 0x00ff0000;		Uint32 bmask = 0x0000ff00;		Uint32 amask = 0x000000ff;	#else		Uint32 rmask = 0x000000ff;		Uint32 gmask = 0x0000ff00;		Uint32 bmask = 0x00ff0000;		Uint32 amask = 0xff000000;	#endif		        //Here is where the magic happen. Create a new surface that is 32 bpp.	texture = SDL_CreateRGBSurface(SDL_SWSURFACE, tex_width, tex_height, 32, rmask, gmask, bmask, amask);		        //Do something with the alphachannel of the loaded image.	SDL_SetAlpha(image, 0, 0);		        //And blit the loaded image onto the newly created surface	SDL_BlitSurface(image, 0 ,texture, 0);		        //And then create a new texture in the usual way	glGenTextures(1, &image_res->TexId);	glBindTexture(GL_TEXTURE_2D, image_res->TexId);			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);				GLenum internal_format;	GLenum img_format, img_type;				switch (texture->format->BitsPerPixel)	{		case 32: 			img_format = GL_RGBA; 			img_type = GL_UNSIGNED_BYTE;			internal_format = GL_RGBA8; 			break;		case 24: 			img_format = GL_RGB; 			img_type = GL_UNSIGNED_BYTE;			internal_format = GL_RGB8;			break;		case 16: 			img_format = GL_RGBA; 			img_type = GL_UNSIGNED_SHORT_5_5_5_1;			internal_format = GL_RGB5_A1; 			break;		default: 			img_format = GL_LUMINANCE; 			img_type = GL_UNSIGNED_BYTE;			internal_format=GL_LUMINANCE8; 			break;	}			glTexImage2D(GL_TEXTURE_2D, 0, internal_format, tex_width, tex_height, 0, img_format, img_type, texture->pixels);			        //Don't forget to free the surfaces, or PETA will get you!	SDL_FreeSurface(image);	SDL_FreeSurface(texture);		return image_res;		}


Look, I even added some comments for you [smile]. If you have any questions I can try and answer them, but as I said it's been a while since I wrote this code.
This is how we did it:

GLuint Platform::loadTexture(std::string name){	std::stringstream pathStream;	pathStream << "data/" << name << ".png";	std::string path = pathStream.str();	SDL_Surface *surface = IMG_Load(path.c_str());	if (!surface)	{		std::cout << "Error loading " << path << ": " << SDL_GetError() << std::endl;		return 0;	}		if (surface->w != surface->h)	{		std::cout << "Error loading " << path << ": texture is niet vierkant" << std::endl;		return 0;	}	if (!isPowerOf2(surface->w))	{		std::cout << "Error loading "<< path << ": texture is ongeldig formaat" << std::endl;		return 0;	}	int imageSize = surface->w * surface->h * surface->format->BytesPerPixel;	GLubyte *data = new GLubyte[imageSize];		int pos = 0;	bool hasAlpha = surface->format->BytesPerPixel == 4;	for (int y = surface->h - 1; y >= 0; y--)	{		for (int x = 0; x < surface->w; x++)		{			Uint8 r, g, b, a;			Uint32 color = getPixel(surface, x, y);						if (hasAlpha)				SDL_GetRGBA(color, surface->format, &r, &g, &b, &a);			else				SDL_GetRGB(color, surface->format, &r, &g, &b);						data[pos++] = r;			data[pos++] = g;			data[pos++] = b;						if (hasAlpha)				data[pos++] = a;		}	}		glGetError();		GLuint id;	glGenTextures(1, &id);	glBindTexture(GL_TEXTURE_2D, id);		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);		int type = hasAlpha ? GL_RGBA : GL_RGB;	glTexImage2D(GL_TEXTURE_2D, 0, type, surface->w, surface->h, 0, type, GL_UNSIGNED_BYTE, data);	GLenum error = glGetError();	if (error != GL_NO_ERROR)		std::cout << "Error #" << error << " while loading " << path << std::endl;		delete[] data;	SDL_FreeSurface(surface);		return id;}
Thanks for the help everyone!!! =D

I managed to do it using SDL_Image and it works great!

Thanks again,

Victor Freire

This topic is closed to new replies.

Advertisement