Incorrectly Drawing Pixmaps

Started by
4 comments, last by BrianMJC 13 years, 8 months ago
Hi,

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):
screen shot

What the png image is supposed to look like (yes, it is the Code::Blocks logo):
screen shot

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 &lt;&lt; <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 &gt; -<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(&amp;png_ptr, &amp;info_ptr, <span class="cpp-number">0</span>);<br> fclose(fp);<br> clog &lt;&lt; <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–> 
Advertisement
BrianMJC, I know this bug...

It's not in code that loads png...

It's OpenGL's bug...

When I colliding with it I'm resizing the image (*2 or /2) while { picture is incorrect }...

I don't know the algorithm that detects "right image dimensions for OpenGL"...

Maybe problem raises when video pages set for store picture incorrectly/
If it's hard to believe me...
Maybe you first try to use it on GDI to make sure that your code is correct!
Thanks FXACE. GDI is a good idea, but I already confirmed that I am reading in the correct data from the png file by manually inspecting the pixel data retrieved with the visual png image. I suspect OpenGL is somehow not interpreting that data correctly. I hope it's not an unsolved bug though, it does seem unlikely since OpenGL is widely used and a fix would have been issued quickly I would think for something as simple as rendering a 2D pixmap onscreen?

I tried changing the type and format for the hell of it (since they are correct afaik) and got nowhere; GL_UNSIGNED_BYTE for example resulted in a slightly different garbled mess. Also, I don't think it's my graphics adapter, but fyi I have an ATI Radeon 4670 with the latest Catalyst drivers.
From what I can see, this is the typical unaligned data problem. By default, OpenGL requires each row to start on a byte-offset that is a multiple of four bytes. Your image is 150 pixels wide, and if it's an RGB image, each row is 150*3=450 bytes wide, which is not a multiple of 4. In that case, OpenGL assumes there are 2 bytes padding at the end of each row, and skips 2 additional bytes before starting to read the following row.

OpenGL is behaving exactly as it should, but you don't provide the data as OpenGL is configured to read it. Thus, the bug is neither with OpenGL or your PNG loader, but with the fact that the data loaded is not directly compatible with OpenGL. Either ensure that each row is aligned properly, or adjust the unpack alignment parameter (see glPixelStore and GL_UNPACK_ALIGNMENT).
Thanks so much, Brother Bob. That sounds right on, I'll check it out later. I knew I was missing something about getting the data onscreen correctly...

This topic is closed to new replies.

Advertisement