png transparency troubles

Started by
5 comments, last by Boblin 20 years, 11 months ago
Ok, right now I am working on a solo project where I am doing a 2D Asteroids clone in OpenGL. I am using libpng to load and read the png images into OpenGL. For some reason or another, I cannot get the png file to display the alpha channel correctly. I doubt it is an OpenGL problem, since I can make the geometry I am slapping the texture on transparent if I wanted. Has anyone messed with libpng and/or OpenGL? I think I am missing something somewhere; just not exactly sure where. Thank you guys in advance.
Advertisement
Only Breifly. I cannot really help you, but I know of an example

Here
Look for the project, "glpng"...

You can use it to see how your image displays in another program using OpenGL and libpng.


I seem to remeber something about OpenGL''s transparency is different than png''s, but I could just be making stuff up... Its late.
~~~~~Screaming Statue Software. | OpenGL FontLibWhy does Data talk to the computer? Surely he's Wi-Fi enabled... - phaseburn
You didn't post any code, but I'll post my png loading code here if it helps you


    #include <png.h>class Image {	int width, height;	int bytes_per_pixel; // 1, 3 or 4	std::vector<unsigned char> data;	void resize(int w, int h, int c);		bool load_tga(const char *filename);	bool load_png(const char *filename);	bool load_jpg(const char *filename);	public:	Image();	Image(const char *filename);	Image(int w, int h, int c);		int getWidth() const;	int getHeight() const ;	int getComponents() const;	unsigned int getPixel(int x, int y) const;	void setPixel(int x, int y, unsigned int value);	const unsigned char *getData();		void power2(); // Scale down to nearest power of two 2^n x 2^m size.	void halve(); // Halve image (if it is in power of 2 dimensions)		bool load(const char *filename);		enum { BuildMipMaps=1 }; 		static bool glTexture2D(const char *filename, int flags);};/* read a png file.  You may want to return an error code if the read   fails (depending upon the failure). */bool Image::load_png(const char *filename){   FILE *fp=0;   png_structp png_ptr=0;   png_infop info_ptr=0;   bool ok=true;	do	{		/* open the file */		fp = fopen(filename, "rb");		if (!fp) {			ok=false;			break;		}		/* Create and initialize the png_struct with the desired error handler		functions.  If you want to use the default stderr and longjump method,		you can supply NULL for the last three parameters.  We also check that		the header file is compatible with the library version.		*/		png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);		if (!png_ptr) {			ok=false;			break;		}		info_ptr = png_create_info_struct(png_ptr);		if (!info_ptr) {			ok=false;			break;		}		/* set error handling if you are using the setjmp/longjmp method */		if (setjmp(png_ptr->jmpbuf))		{			ok=false;			break;		}		/* set up the input control if you are using standard C streams */		png_init_io(png_ptr, fp);		/* read the file information */		png_read_info(png_ptr, info_ptr);		/* set up the transformations you want.  Note that these are all optional.  Only call them if you want them */		/* expand paletted colors into true RGB triplets */		if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) png_set_expand(png_ptr);		/* expand grayscale images to the full 8 bits */		if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY && info_ptr->bit_depth < 8) png_set_expand(png_ptr);		/* expand paletted or RGB images with transparency to full alpha channels		* so the data will be available as RGBA quartets */		if (info_ptr->valid & PNG_INFO_tRNS) png_set_expand(png_ptr);		/* tell libpng to handle the gamma conversion for you */		double screen_gamma = 2.0;		if (info_ptr->valid & PNG_INFO_gAMA)			png_set_gamma(png_ptr, screen_gamma, info_ptr->gamma);		else			png_set_gamma(png_ptr, screen_gamma, 0.45);		/* PNG can have files with 16 bits per channel.  If you only can handle		8 bits per channel, this will strip the pixels down to 8 bit. */		if (info_ptr->bit_depth == 16)			png_set_strip_16(png_ptr);		/* pack multiple pixels with bit depths of 1, 2, and 4 into bytes */		if (info_ptr->bit_depth<8)			png_set_packing(png_ptr);		/* optional call to gamma correct and add the background to the palette and update info structure. */		png_read_update_info(png_ptr, info_ptr);		if (info_ptr->channels!=1 && info_ptr->channels!=3 && info_ptr->channels!=4) {			printf("File %s has %d channels.\n",filename, info_ptr->channels);			printf("Try using 1, 3 or 4 channels.\n");			ok = false;			break;		}		if (info_ptr->bit_depth!=8) {			printf("File %s has bit depth %d.\n",filename, info_ptr->bit_depth);			printf("Only bit depth of 8 is supported.\n");			ok = false;			break;		}		if (info_ptr->color_type!=PNG_COLOR_TYPE_RGB && info_ptr->color_type!=PNG_COLOR_TYPE_GRAY && info_ptr->color_type!=PNG_COLOR_TYPE_RGB_ALPHA) {			printf("File %s has unsupported color type.\n",filename);			printf("Types GRAY, RGB and RGB_ALPHA are supported.\n");			ok = false;			break;		}		/* the easiest way to read the image */		vector<png_bytep> row_pointers;		row_pointers.resize(info_ptr->height);		if (row_pointers.size()!=info_ptr->height) {			ok = false;			break;		}		vector<png_byte> buffer;		buffer.resize(info_ptr->height*info_ptr->rowbytes);		if (buffer.size()!=(info_ptr->height*info_ptr->rowbytes)) {			ok = false;			break;		}		for (unsigned int row = 0; row < info_ptr->height; row++)			row_pointers[row] = &buffer[row*info_ptr->rowbytes];		png_read_image(png_ptr, &row_pointers[0]);		/* allocate the memory to hold the image using the fields of png_info. */		this->resize(info_ptr->width, info_ptr->height,info_ptr->channels);		for (unsigned int row = 0; row < info_ptr->height; row++)		{			unsigned char *ptr = row_pointers[row];			for (unsigned int x=0; x<info_ptr->width; x++)			{				unsigned int value = 0;				if (getComponents()==4) value = rgba2uint(ptr[0],ptr[1],ptr[2],ptr[3]);				if (getComponents()==3) value = rgb2uint(ptr[0],ptr[1],ptr[2]);				if (getComponents()==1) value = lumin2uint(ptr[0]);				setPixel(x,row,value);				ptr += info_ptr->channels;			}		}		buffer.clear();		/* read the rest of the file, getting any additional chunks in info_ptr */		png_read_end(png_ptr, info_ptr);	} while (false);	/* clean up after the read, and free any memory allocated */	if (png_ptr) png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);	/* close the file */	if (fp) fclose(fp);	/* that's it */	return ok;}bool Image::load(const char *filename){    width=height=0;	bytes_per_pixel=1;	data.clear();	if (!filename) return false;	if( strlen(filename)==0) return false;	string path = filename;	string ext = path.substr(path.rfind(".")+1,path.length());	ToLower lo;    transform(ext.begin(), ext.end(), ext.begin(), lo);	if      (ext=="tga") return load_tga(filename);	else if (ext=="png") return load_png(filename);	else if (ext=="jpg") return load_jpg(filename);    return false;}Image::Image(const char *filename) : width(0), height(0), bytes_per_pixel(1), data(){	load(filename);}bool Image::glTexture2D(const char *filename, int flags){	Image img(filename);	img.power2();	int format;	if (img.getComponents()==1) format = GL_LUMINANCE;	else if (img.getComponents()==3) format = GL_RGB;	else if (img.getComponents()==4) format = GL_RGBA; // voisi valita tarkemmin jos on 16 bittinen!!	else return false;	if (flags & BuildMipMaps)		gluBuild2DMipmaps(GL_TEXTURE_2D, img.getComponents(), img.getWidth(), img.getHeight(), format, GL_UNSIGNED_BYTE, &img.data[0]);	else		glTexImage2D(GL_TEXTURE_2D, 0, img.getComponents(), img.getWidth(), img.getHeight(), 0, format, GL_UNSIGNED_BYTE, &img.data[0]);	return true;}    


This works with alpha-channel for me

[edited by - stefu on April 30, 2003 2:48:59 AM]
Below is the function that I am using to load a png into OpenGL. Also, I noticed a double for loop after you read in the file, stefu. What exactly does that do? My brain doesn''t work too well at 3:44 in the AM. :-) Thank you again.

  void Game::CreatePNGTexture(UINT textureArray[], LPSTR strFileName, int textureID){	// Open the PNG file to read.	FILE *fp = fopen(strFileName, "rb");	unsigned char header[PNG_HEADERSIZE] = {0};	bool isPng;	double dGamma = NULL;	// get all the info we need for the image.	unsigned long rowBytes ;	int pngHt;	int pngWdth;	int bitDepth;	int colorType;	// Make sure we have a file.	if(!fp) return;	// Read in the header.	if(fread(header, 1, PNG_HEADERSIZE, fp) != PNG_HEADERSIZE)	{		return;	}	// Check to see if the header is valid.	isPng = !png_sig_cmp(header, 0, PNG_HEADERSIZE);	// Makes sure we have a valid PNG file.	if(!isPng) return;	// this creates the pointer that we need in order to load the PNG to our program.	// the NULL parameters passed, i believe can be replaced with function pointers,	// for custom error handling.	png_structp pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);	// Makes sure we have a valid PNG pointer	if(!pngPtr) return;	// Creates a pointer that stores info about the PNG file we want to load.	png_infop infoPtr = png_create_info_struct(pngPtr);	// Makes sure we have a valid info pointer.	if(!infoPtr)	{		png_destroy_read_struct(&pngPtr, (png_infopp)NULL, (png_infopp)NULL);		return;	}	// The end info pointer... yeah I''m not too sure what it does either.	png_infop endInfo = png_create_info_struct(pngPtr);	// Makes sure we have a valid pointer.	if(!endInfo)	{		png_destroy_read_struct(&pngPtr, &infoPtr, (png_infopp)NULL);		return;	} 	// More error checkin''	if(setjmp(pngPtr->jmpbuf))	{		png_destroy_read_struct(&pngPtr, &infoPtr, (png_infopp)NULL);// &endInfo);		fclose(fp);		return;	}	// initialize the reading process of the PNG file.	png_set_read_fn(pngPtr, (png_voidp)fp, png_read_data);	// Since we read the file earlier, we need to reset the write cursor back to the beginning.	png_set_sig_bytes(pngPtr, PNG_HEADERSIZE);	// The reading of the PNG info.	png_read_info(pngPtr, infoPtr);	png_get_IHDR(pngPtr, infoPtr, (unsigned long *)&pngWdth, (unsigned long *)&pngHt, &bitDepth, &colorType, NULL, NULL, NULL);	rowBytes = png_get_rowbytes(pngPtr, infoPtr);	// if required set gamma conversion	//png_color_16 myBG;	//png_set_background(pngPtr, &myBG, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);    if (png_get_gAMA(pngPtr, infoPtr, &dGamma))	{		png_set_gamma(pngPtr, (double) 2.2, dGamma);	}	if((colorType & PNG_COLOR_TYPE_RGB_ALPHA) != PNG_COLOR_TYPE_RGB_ALPHA)	{		int i = 0;	}	// MAkes sure we have the most current info.	png_read_update_info(pngPtr, infoPtr);	/////////////////////////////////////////////////////////////////////////////////////////																// Where the image is going to be stored.	png_bytepp rowPtrs;	unsigned char *imgData;	// Making memory for the image.	imgData = (png_byte *) malloc(rowBytes * (pngHt) * sizeof(png_byte));	// Making memory for the image.	rowPtrs = (png_bytepp)malloc((pngHt) * sizeof(png_bytep));	// Makes the above two pointers work together so we can have an image both in a char * and	// a char **	for (int i = 0; i < (pngHt); i++)	{		// This has to be done so we can flip the image correctly		int j = pngHt - i - 1;		rowPtrs[j] = imgData + i * rowBytes;	}	// Readin'' the image in.	png_read_image(pngPtr, rowPtrs);  	// we are done readin''!	png_read_end(pngPtr, endInfo);	/////////////////////////////////////////////////////////////////////////////////////	// OpenGL image texture loading	/////////////////////////////////////////////////////////	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, pngWdth, pngHt, 0, GL_RGBA, GL_UNSIGNED_BYTE, 		imgData);	glGenTextures(1, &textureArray[textureID]);	glBindTexture(GL_TEXTURE_2D, textureArray[textureID]);	gluBuild2DMipmaps(GL_TEXTURE_2D, 3, pngWdth, pngHt, GL_RGBA, GL_UNSIGNED_BYTE, imgData);	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR_MIPMAP_LINEAR);	/////////////////////////////////////////////////////////		// get rid of the png struct we created earlier.	png_destroy_read_struct(&pngPtr, &infoPtr, (png_infopp)NULL);// &endInfo);	// Get rid of the image in main memory.	delete [] imgData;	delete [] rowPtrs;		// Close the file!			  	fclose(fp);	  }  
Well, it took for me too some time to recall what the double loop was doing
For every pixel in png-file, it get's the color components (1,3 or 4 respectively for gery, rghb or rgba) and then call setPixel to set pixel value for the Image object.
It could be done much better (copy each row directly). IIRC I had before different Image data structure and thus it was not then possible. So I need to optimize this, because I have noticed it loads quite slowly all png's


About your code:

This does nothing!!if((colorType & PNG_COLOR_TYPE_RGB_ALPHA) != PNG_COLOR_TYPE_RGB_ALPHA)	{		int i = 0;	}But I can see the mistake here:	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, pngWdth, pngHt, 0, GL_RGBA, GL_UNSIGNED_BYTE, 		imgData);	glGenTextures(1, &textureArray[textureID]);	glBindTexture(GL_TEXTURE_2D, textureArray[textureID]);	gluBuild2DMipmaps(GL_TEXTURE_2D, 3, pngWdth, pngHt, GL_RGBA, GL_UNSIGNED_BYTE, imgData);   

Use only glTexImage2D or gluBuild2DMipmaps!!! Not both.

So first genTexture, then BindTexture and then gluBuild2DMipmaps or glTexImage2D!

And look at this again:
gluBuild2DMipmaps(GL_TEXTURE_2D, 3 <====!!!!!!!!!!, pngWdth, pngHt, GL_RGBA, GL_UNSIGNED_BYTE, imgData);
Put there 4

You should also test how many channels does the png have and then use 1, 3 or 4 (GL_LUMINANCE, GL_RGB or GL_RGBA) as parameter ot opengl.

[edited by - stefu on April 30, 2003 4:56:16 AM]
Thank you very much for your advice! As for the following code,
if((colorType & PNG_COLOR_TYPE_RGB_ALPHA) != PNG_COLOR_TYPE_RGB_ALPHA)
{
int i = 0;
}

I did that for debugging purposes. I will try those things and see what I come up with. Thank you again!
Damn those copy and paste errors! The following was my main problem

gluBuild2DMipmaps(GL_TEXTURE_2D, 3 <====!!!!!!!!!!, pngWdth, pngHt, GL_RGBA, GL_UNSIGNED_BYTE, imgData);
Put there 4

I copied this code from something I did earlier while working with bitmaps. Yeah you can tell I was working on this code late at night :-P

This topic is closed to new replies.

Advertisement