I'm not 100% sure if this is a libpng problem or an OpenGL problem, but I am pretty certain it's an OpenGL problem. I'm loading a png file and trying to draw it onscreen with OpenGL with SDL. I can confirm that SDL and OpenGL are both setup properly, and after confirming that a png load function I wrote with libpng correctly copies a png file's image data into a GLbyte * using libpng, I am at a loss as to why my calls to glDrawPixels() result in gibberish being rendered to the screen. It gets the correct width, height and raster position, but the image itself is a garbled mess.
Any ideas? Thanks.
OS: Windows XP Pro SP3
IDE/Compiler: Code::Blocks 10.05 with included GNU GCC (MinGW)
Also note: I refer to libpnge in the code below, which is my own library that takes libpng and sticks basic functions like load, save and draw into a png class. It's compiled as a static library, and used for an executable I'm developing at the same time I develop libpnge.
Screen shot of the gibberish (rendering a 150x120 pixel image to a 1280x768 window):
What the png image is supposed to look like (yes, it is the Code::Blocks logo):
My rendering code (in libpnge.h):
class png { public: ... void draw(GLfloat xpos, GLfloat ypos) { if (_pixmap) { glRasterPos2f(xpos, ypos); glDrawPixels(_width, _height, _format, _type, _pixmap); } } ... private: void load(const string &filename); // called in constructor GLsizei _width, _height; GLenum _format, _type; GLbyte *_pixmap; };
My png load function (in libpnge.cpp):
void png::load(const string &filename) { // open the file for reading FILE *fp; fp = fopen(filename.c_str(), "rb"); if (!fp) { throw "cpng::load() - Failed to open file"; }#ifdef VERBOSE_LIBPNGE clog << "\ncpng::load() - File opened successfully\n";#endif // make sure it's png const size_t sig_bytes = 4; size_t bytes_read; png_byte buf[sig_bytes]; if ((bytes_read = fread(buf, 1, sig_bytes, fp)) != sig_bytes) { fclose(fp); error = "cpng::load() - Failed to read file's PNG signature, "; error += bytes_read + " bytes were read in instead of 4"; throw error; }#ifdef VERBOSE_LIBPNGE clog << "cpng::load() - File's PNG signature read in successfully\n";#endif int sig_cmp = !png_sig_cmp(buf, (png_size_t)0, (png_size_t)sig_bytes); if (!sig_cmp) { fclose(fp); throw "cpng::load() - File has an invalid PNG signature"; }#ifdef VERBOSE_LIBPNGE clog << "cpng::load() - File has a valid PNG signature\n";#endif // create libpng structs for storing pixmap data and parameters, we'll put the needed data into the png class instance later png_structp png_ptr; png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); if (!png_ptr) { fclose(fp); throw "cpng::load() - Failed to create libpng read struct"; }#ifdef VERBOSE_LIBPNGE clog << "cpng::load() - Successfully created libpng read struct\n";#endif png_infop info_ptr; info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr, 0, 0); fclose(fp); throw "cpng::load() - Failed to create libpng info struct"; }#ifdef VERBOSE_LIBPNGE clog << "cpng::load() - Successfully created libpng info struct\n";#endif // libpng expects to longjump back to this routine (cpng::load) on error, this sets it up to do so if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, 0); fclose(fp); throw "cpng::load() - Failed to set default error handling"; }#ifdef VERBOSE_LIBPNGE clog << "cpng::load() - Successfully set default error handling\n";#endif // initialize libpng file i/o so it sees the file, and inform libpng to advance its own file ptr since the png sig was // already read in png_init_io(png_ptr, fp); png_set_sig_bytes(png_ptr, sig_bytes);#ifdef VERBOSE_LIBPNGE clog << "cpng::load() - Successfully initialized libpng i/o and advanced its file pointer to pass the PNG signature\n";#endif // read pixmap parameters such as byte width, byte height and pixel format from IHDR chunk into class instance png_read_info(png_ptr, info_ptr); // put info from png chunks prior to the image data into info_ptr png_uint_32 width, height; int bit_depth, color_type; png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0); // get needed info from IHDR chunk if (16 == bit_depth) { png_set_strip_16(png_ptr); bit_depth = 8; clog << "cpng::load() - File uses 16 bits per channel, striping down to 8 and continuing"; } if (8 != bit_depth) // ensure file uses 8 bits per channel { png_destroy_read_struct(&png_ptr, &info_ptr, 0); fclose(fp); error = "cpng::load() - Only 8 bit color channels are supported, this file uses "; error += bit_depth + " bit"; throw error; }#ifdef VERBOSE_LIBPNGE clog << "cpng::load() - Confirmed that the file uses 8 bit color depth\n";#endif if (PNG_COLOR_TYPE_RGB == color_type) // 24 bit RGB color format { _format = GL_RGB; } else if (PNG_COLOR_TYPE_RGBA == color_type) // 32 bit RGBA color format { _format = GL_RGBA; } else { png_destroy_read_struct(&png_ptr, &info_ptr, 0); fclose(fp); throw "cpng::load() - File does not use RGB or RGBA color format, grayscale and palette formats unsupported"; }#ifdef VERBOSE_LIBPNGE clog << "cpng::load() - Confirmed that the file uses a supported pixel format\n";#endif _type = GL_BYTE; // OpenGL needs to know _pixmap is a signed byte array (GLbyte) _width = (GLsizei)width; // now store pixel width, height and bit depth _height = (GLsizei)height; // prepare to read the image data chunk into memory png_bytep temp_pixmap[height]; // need array of ptrs to work with libpng's png_read_image() png_uint_32 bytes_per_row = png_get_rowbytes(png_ptr, info_ptr); int i; for (i = 0; i < (int)height; i++) { temp_pixmap = new png_byte[bytes_per_row]; if (!temp_pixmap) { png_destroy_read_struct(&png_ptr, &info_ptr, 0); fclose(fp); error = "cpng::load() - Failed to allocate temporary memory block, array index #"; error += i; throw error; } }#ifdef VERBOSE_LIBPNGE clog << "cpng::load() - Successfully allocated temporary memory blocks for the image data chunk\n";#endif // read the image data into temporary storage, because pixmap must be byte ptr and png_read_image only works with array of // byte ptrs (each pixmap row is allocated in a different memory block, whereas my pixmap is one consecutive memory block) png_read_image(png_ptr, temp_pixmap); // copy image data into temporary memory#ifdef VERBOSE_LIBPNGE clog << "cpng::load() - Successfully copied the image data to the temporary memory blocks\n";#endif unsigned long size = (unsigned long)(height * bytes_per_row); // calculate how many bytes to allocate for pixmap _pixmap = new GLbyte[size]; if (!_pixmap) { png_destroy_read_struct(&png_ptr, &info_ptr, 0); fclose(fp); throw "cpng::load() - Failed to allocate pixmap"; }#ifdef VERBOSE_LIBPNGE clog << "cpng::load() - Successfully allocated memory for the pixmap\n";#endif for (i = 0; i < (int)height; i++) // then copy temporary memory to pixmap { memcpy(&_pixmap, temp_pixmap<span style="font-weight:bold;">, bytes_per_row);<br> }<br><span class="cpp-directive">#ifdef</span> VERBOSE_LIBPNGE<br> clog << <span class="cpp-literal">"cpng::load() - Successfully copied the image data in the temporary memory blocks to the pixmap\n"</span>;<br><span class="cpp-directive">#endif</span><br><br> <span class="cpp-comment">// free up all resources and return</span><br> <span class="cpp-keyword">for</span> (i = (<span class="cpp-keyword">int</span>)height - <span class="cpp-number">1</span>; i > -<span class="cpp-number">1</span>; i–)<br> {<br> <span class="cpp-keyword">delete</span> [] temp_pixmap<span style="font-weight:bold;">;<br> }<br> png_destroy_read_struct(&png_ptr, &info_ptr, <span class="cpp-number">0</span>);<br> fclose(fp);<br> clog << <span class="cpp-literal">"cpng::load() - Successfully loaded PNG file\n\n"</span>;<br> <span class="cpp-keyword">return</span>;<br><br> }<br><br><br><br><br></pre></div><!–ENDSCRIPT–>