C++ OpenGL+LibPNG - Loading a PNG easily

Whether you're a newbie or an experienced programmer, any questions, help, or just talk of any language will be welcomed here.

Moderator: Coders of Rage

Post Reply
User avatar
RyanPridgeon
Chaos Rift Maniac
Chaos Rift Maniac
Posts: 447
Joined: Sun Sep 21, 2008 1:34 pm
Current Project: "Triangle"
Favorite Gaming Platforms: PC
Programming Language of Choice: C/C++
Location: UK
Contact:

C++ OpenGL+LibPNG - Loading a PNG easily

Post by RyanPridgeon »

SO. One of the things that always annoyed me about using openGL is that if I wanted to load an image I always had to do it through some bloated library like SDL_image or whatever, but of course in the end I tried to do it myself using libpng.

I actually had so many problems with libpng (call me a noob) that I gave up and asked for help, so all the libpng loading here actually belongs mostly to M_D_K.

You will need to download and install zlib and libpng to use this code.

Here is the rather large source code;

Code: Select all

#ifndef PNGLOADEROPENGL
#define PNGLOADEROPENGL

#include <cstdio>
#include <cstdlib>
#include <png.h>
#include <gl/gl.h>




int GetTextureInfo(int ColourType)
{
	int ret;
	switch(ColourType)
	{
		case PNG_COLOR_TYPE_GRAY:
			ret = 1;
		break;
		case PNG_COLOR_TYPE_GRAY_ALPHA:
			ret = 2;
		break;
		case PNG_COLOR_TYPE_RGB:
			ret = 3;
		break;
		case PNG_COLOR_TYPE_RGB_ALPHA:
			ret = 4;
		break;
		default:
			ret = -1;//fucked
	};
	return ret;
};

GLuint loadImage(const char *filename)
{
    GLuint texture;
	png_structp png_ptr = NULL;
	png_infop info_ptr = NULL;
	png_bytep *row_pointers = NULL;
	int bitDepth, colourType;

	FILE *pngFile = fopen(filename, "rb");

	if(!pngFile)
		return 0;

	png_byte sig[8];

	fread(&sig, 8, sizeof(png_byte), pngFile);
	rewind(pngFile);//so when we init io it won't bitch
	if(!png_check_sig(sig, 8))
		return 0;

	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,NULL,NULL);

	if(!png_ptr)
		return 0;

	if(setjmp(png_jmpbuf(png_ptr)))
		return 0;

	info_ptr = png_create_info_struct(png_ptr);

	if(!info_ptr)
		return 0;

	png_init_io(png_ptr, pngFile);

	png_read_info(png_ptr, info_ptr);

	bitDepth = png_get_bit_depth(png_ptr, info_ptr);

	colourType = png_get_color_type(png_ptr, info_ptr);

	if(colourType == PNG_COLOR_TYPE_PALETTE)
		png_set_palette_to_rgb(png_ptr);

	if(colourType == PNG_COLOR_TYPE_GRAY && bitDepth < 8)
		png_set_gray_1_2_4_to_8(png_ptr);

	if(png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
		png_set_tRNS_to_alpha(png_ptr);

	if(bitDepth == 16)
		png_set_strip_16(png_ptr);
	else if(bitDepth < 8)
		png_set_packing(png_ptr);

	png_read_update_info(png_ptr, info_ptr);

	png_uint_32 width, height;
	png_get_IHDR(png_ptr, info_ptr, &width, &height,
			&bitDepth, &colourType, NULL, NULL, NULL);

	int components = GetTextureInfo(colourType);

	if(components == -1)
	{
		if(png_ptr)
			png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
		return 0;
	}

	GLubyte *pixels = (GLubyte *)malloc(sizeof(GLubyte) * (width * height * components));

	row_pointers = (png_bytep *)malloc(sizeof(png_bytep) * height);

	for(int i = 0; i < height; ++i)
        row_pointers[i] = (png_bytep)(pixels + (i * width * components));

	png_read_image(png_ptr, row_pointers);
	png_read_end(png_ptr, NULL);


    // make it
	glGenTextures(1, &texture);
    // bind it
	glBindTexture(GL_TEXTURE_2D, texture);
    // stretch it
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
    // technologic - I MEAN

    // here we has the problems
    GLuint glcolours;
    (components==4) ? (glcolours = GL_RGBA): (0);
    (components==3) ? (glcolours = GL_RGB): (0);
    (components==2) ? (glcolours = GL_LUMINANCE_ALPHA): (0);
    (components==1) ? (glcolours = GL_LUMINANCE): (0);

    glTexImage2D(GL_TEXTURE_2D, 0, components, width, height, 0, glcolours, GL_UNSIGNED_BYTE, pixels);

	png_destroy_read_struct(&png_ptr, &info_ptr, NULL);

	fclose(pngFile);
	free(row_pointers);
	free(pixels);


	return texture;

};

#endif

And here's how you use it;

To load a png into a texture, simply create a GLuint to store the texture's openGL name.

Code: Select all

GLuint myTexture;
Then to load the image, simply use the function;

Code: Select all

myTexture = loadImage("filepath.png")
As you see, the function returns the openGL name of the texture so that you can use it. Here's the fun part; if the loading failed, it will return 0, which means that even if the loading fails, if you try to use the texture you will simply get the default white colour, instead of a segfault or crash.

To then use the texture in your opengl code, just bind your texture name

Code: Select all

glBindTexture(GL_TEXTURE_2D, myTexture);
Thanks and hope it helps y'all

Although I still had problems with it; turns out M_D_K was an evil bastard and made it buggy on purpose so I could learn more xD

Extra thanks to; qphalcyon and Ginto8
Ryan Pridgeon
C, C++, C#, Java, ActionScript 3, HaXe, PHP, VB.Net, Pascal
Music | Blog
Post Reply