Loading PNG Files

Started by
9 comments, last by steven katic 14 years, 11 months ago
I'm writing a 2D graphics library with C++ and OpenGL. It needs to load PNG files into the RAM (as pixel rectangles) before it loads them to the GPU as texture objects. Since I have no knowledge in the fields of compression and dynamic linking, I decided to try an existing library. I found libpng. It does exactly what I need. The problem is that the documentation is a disaster. And it's written in C. Using C versions of libraries I'm already using in C++ wastes memory (bigger EXE file) and I'm not familiar with most of the functions that are used there since I only use their C++ equivalents. After downloading zlib, which is used by libpng, I downloaded png++, which can read a PNG file using one simple function. I wrote a simple C++ program that loads a PNG file and draws it on the screen, but it didn't work. After coying and pasting source files and replacing some include <..> with include "..." , all libraries could be read, but there was a new problem: compilation errors in the libraries. png++ is written for g++ while I'm using Visual C++ so maybe this is why there are errors. Anyway, does anyone know a simple method to load a 32-bit RGBA PNG file into the memory as a block of pixels? Anything would help. Especially a simple example that uses png++ (with Visual C++) and simply reads a RGBA PNG file into the memory (and then loads it to the GPU). A simple explanation will help too. :)
Advertisement
As you've already discovered, using libpng directly is a pain in the ass. You might want to try LodePNG...I've used it in the past and it's very simple.

I've also seen many people mention DevIL which seems to be quite nice, however I've never used it myself so I can't comment too much on it.
If you want to stick with libpng, this page should help. It's for MAC, but should work anyway.
http://www.macdevcenter.com/pub/a/mac/2005/10/14/texture-maps.html?page=2
I would recommend stb_image. A public domain image library. Only 3800 lines of code (does not require zlib or anything) and can load JPEG, PNG, BMP, TGA, PSD, HDR, and can write BMP or TGA.

https://mollyrocket.com/forums/viewtopic.php?t=315
[size="1"]Perl - Made by Idiots, Java - Made for Idiots, C++ - Envied by Idiots | http://sunray.cplusplus.se
As long as you wrap libpng into its own library (compiled it as a static library in the form of a DLL file), then you could care less whether it was written in C or C++.
Export the functions you need, and that's all you have to do. You might have problems trying to drop it into your own project space, so just take my advice it, wrap it,
and keep it separate. The same thing goes with zlib and other 3rd party libraries. You will have less problems.
Also the 'Simple OpenGL Image Library' may be of interest for you.

Hi,

I'm the author of PNG++ :)

It would be nice if you could send me the compilation error message you get along with the sample code you've been compiling. PNG++ isn't written for g++, however, I must admit I didn't test Visual Studio compliance in a while...

Please use this email address: pngpp-devel@nongnu.org

--
Regards,
Alex
This is what I currently use, it's rough as guts and just wraps the libpng calls to give a c++ function. Limited or no error handling and only a subset of pixel types - it basically only works. My Renderable class is just a std::vector of agg rgba32 pixels. You will want to change this to your own representation but you can see how the pixels are set in the code. Also remove the timer stuff which I was using because I was loading some big textures.

header.h
#ifndef LIBPNG_H#define LIBPNG_H#include <boost/shared_ptr.hpp>struct Renderable;boost::shared_ptr< Renderable>  read_png_file( const std::string & file_name );#endif



source.cpp
// g++ main.cpp  libpng.a -lm -lz#include <iostream>#include <cassert>#include <vector>#include <string>#include <stdexcept>//#include <unistd.h>#include <stdlib.h>#include <stdio.h>#include <string.h>#include <stdarg.h>#include "libpng.h"		// our interface#include "renderable.h"		#include "timer.h"#define PNG_DEBUG 3#include <png.h>void abort_(const char * s, ...){	va_list args;	va_start(args, s);	vfprintf(stderr, s, args);	fprintf(stderr, "\n");	va_end(args);	abort();}boost::shared_ptr< Renderable>  read_png_file( const std::string &filename)//void read_png_file( const char *filename, Renderable &buffer){	start_timer();	boost::shared_ptr< Renderable>	buffer( new Renderable); 	png_structp		png_ptr;	png_infop		info_ptr;	int number_of_passes;	char header[8];	// 8 is the maximum size that can be checked	/* open file and test for it being a png */	FILE *fp = fopen( filename.c_str(), "rb");	if (!fp) { 		//	abort_("[read_png_file] File %s could not be opened for reading", filename);		throw std::runtime_error( "could not open file");	}		fread(header, 1, 8, fp);	if (png_sig_cmp( (png_byte *)header, 0, 8))		abort_("[read_png_file] File %s is not recognized as a PNG file", filename.c_str());	/* initialize stuff */	png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);		if (!png_ptr)		abort_("[read_png_file] png_create_read_struct failed");	info_ptr = png_create_info_struct(png_ptr);	if (!info_ptr)		abort_("[read_png_file] png_create_info_struct failed");	if (setjmp(png_jmpbuf(png_ptr)))		abort_("[read_png_file] Error during init_io");	// we need gamma handling of color space somewhere 	// ?? 	png_init_io(png_ptr, fp);	png_set_sig_bytes(png_ptr, 8);	png_read_info(png_ptr, info_ptr);	number_of_passes = png_set_interlace_handling(png_ptr);	png_read_update_info(png_ptr, info_ptr);	// bloody hell what a messy interface 	// does it always have an alpha or not an alpha ??	// we want to initialize the io with a pure memory buffer	// the thing is not alpha channedled 	// this is not critical 	assert( sizeof( png_bytep) == 8);	/* read file */	if (setjmp(png_jmpbuf(png_ptr)))		abort_("[read_png_file] Error during read_image");#if 0	// we have the bitdepth ... ie 4 or 8	// but how do we classify -- ugghhh 	std::cout << "------------------------"  << "\n"; 	std::cout << "width/height " << info_ptr->width << ", " << info_ptr->height  << "\n" ; 	std::cout << "color_type   " << (unsigned)info_ptr->color_type << "\n"; 	std::cout << "bit_depth    " << (unsigned)info_ptr->bit_depth << "\n"; 	std::cout << "rowbytes     " << info_ptr->rowbytes  << std::endl;#endif	// what type of structure are we going to use ??	std::vector< png_byte >		buf( info_ptr->height * info_ptr->rowbytes);		std::vector< png_byte*>		rows( info_ptr->height);		for( unsigned y = 0; y < rows.size(); ++y)		rows[ y] = & buf[ y * info_ptr->rowbytes];	png_read_image( png_ptr, &rows[ 0]);    fclose(fp);	buffer->resize( info_ptr->width, info_ptr->height );	for( unsigned y = 0; y < rows.size(); ++y)	{		png_byte *row = rows[ y];		unsigned x = 0;		if( info_ptr->color_type == PNG_COLOR_TYPE_RGB 			&& info_ptr->bit_depth == 8) {  			for( unsigned i = 0; i < info_ptr->width * 3; i += 3)			{				assert( i + 2 < info_ptr->rowbytes);								// potentially need to adjust bitdepth to our 0xff range				unsigned char r = row; <br>				<span class="cpp-keyword">unsigned</span> <span class="cpp-keyword">char</span> g = row; <br>				<span class="cpp-keyword">unsigned</span> <span class="cpp-keyword">char</span> b = row; <br>				<span class="cpp-keyword">unsigned</span> <span class="cpp-keyword">char</span> a = 0xff;<br><br>				buffer-&gt;rbase.copy_pixel( x, y, agg::rgba8( r, g, b, a)); <br>				++x;<br>			}<br>		}<br>		<span class="cpp-keyword">else</span> <span class="cpp-keyword">if</span> (info_ptr-&gt;color_type == PNG_COLOR_TYPE_GRAY<br>			&amp;&amp; info_ptr-&gt;bit_depth == <span class="cpp-number">8</span>)<br>		{<br>			<span class="cpp-keyword">for</span>( <span class="cpp-keyword">unsigned</span> i = <span class="cpp-number">0</span>; i &lt; info_ptr-&gt;width ; i ++)<br>			{<br>				<span class="cpp-comment">// potentially need to adjust bitdepth to our 0xff range</span><br>				<span class="cpp-keyword">unsigned</span> <span class="cpp-keyword">char</span> r = row; <br>				<span class="cpp-keyword">unsigned</span> <span class="cpp-keyword">char</span> a = 0xff;<br><br>				<span class="cpp-comment">// there's not enough range ?</span><br>				<span class="cpp-comment">// r = (r / 2) + 0x7f; </span><br><br>				buffer-&gt;rbase.copy_pixel( x, y, agg::rgba8( r, r, r , a)); <br>				++x;<br>			}<br>		}<br>		<span class="cpp-keyword">else</span> assert( <span class="cpp-number">0</span>);<br>	}<br><br><br><br><span class="cpp-comment">//	std::cout &lt;&lt; "file '" &lt;&lt; filename &lt;&lt; "' load time " &lt;&lt; elapsed_time() &lt;&lt; "ms\n"; </span><br><br><br>	<span class="cpp-keyword">return</span> buffer;<br>}<br></pre></div><!–ENDSCRIPT–><br>
Are we assuming that you require a cross platform solution?

//If not, there is the windows specific library
System::Drawing::Bitmap
// that can load png files and
System::Drawing::Imaging;
//for accessing (the bits of) BitmapData of the loaded image
Quote:Original post by steven katic
Are we assuming that you require a cross platform solution?

//If not, there is the windows specific library
System::Drawing::Bitmap
// that can load png files and
System::Drawing::Imaging;
//for accessing (the bits of) BitmapData of the loaded image


That's hardly C++ compatible.

This topic is closed to new replies.

Advertisement