Jump to content
  • Advertisement
Sign in to follow this  
BrianMJC

OpenGL Incorrectly Drawing Pixmaps

This topic is 2974 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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[i * bytes_per_row], temp_pixmap, bytes_per_row);
}
#ifdef VERBOSE_LIBPNGE
clog << "cpng::load() - Successfully copied the image data in the temporary memory blocks to the pixmap\n";
#endif

// free up all resources and return
for (i = (int)height - 1; i > -1; i--)
{
delete [] temp_pixmap;
}
png_destroy_read_struct(&png_ptr, &info_ptr, 0);
fclose(fp);
clog << "cpng::load() - Successfully loaded PNG file\n\n";
return;

}




Share this post


Link to post
Share on other sites
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/

Share this post


Link to post
Share on other sites
If it's hard to believe me...
Maybe you first try to use it on GDI to make sure that your code is correct!

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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).

Share this post


Link to post
Share on other sites
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...

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!