This is probably just a simple typing mistake somewhere, but I''ve been staring at my code for a couple hours and finally just gave up =[
Here''s the problem: I''m working on the preliminary stages for a GUI for my game and I need to load images for buttons and pictures and whatnot. Naturally, images in an interface can''t always conform to the power-of-two rule for texture dimensions in OpenGL, and glDrawPixels() is far too slow to be useful. My solution was to split an image into a bunch of pieces that DO have power-of-two dimensions, then load them as textures and draw them together. This worked fine for the first image I loaded (256x256, which didn''t use much of the splitting code, heh), but when I tried to load an 800x600 image, something went horribly awry.
The program crashes when I call glTexImage2D() for the third piece, which is 32x512 pixels. Here is the code for the loading function.
bool GraphicsDataManager::LoadBitmap( const Texture& t, TextureHandle* handle )
{
// Make sure this texture is loadable
// Textures without names and without any graphical data are not usuable
if ( t.name.Length() == 0 ||
t.width == 0 ||
t.height == 0 ||
t.data == NULL )
{
game->errorlog->Log( "GraphicsDataManager", "Attempt to load unusable texture", 2 );
return false;
}
// Load t into the list before anything happens to it
// All textures and bitmaps are loaded into a linked list "textures"
TextureHandle h;
textures.AddAtHead( t );
h = &(textures.Begin()->data); // we will refer to the image to be loaded as "h" from now on
if ( handle != NULL )
*handle = h;
// Figure out how many images we are going to split t into
// Since OpenGL can only handle textures with dimensions that are
// powers of two, we must decompose the bitmap into a bunch of smaller
// textures. Since each bit in a binary number is a power of two, we
// simply figure out how many bits are "on" in the image''s dimensions.
unsigned char wbits = 0; // bits in the width
unsigned char hbits = 0; // bits in the height
for ( int i = 15; i >= 0; i-- ) // width and height are 16-bit unsigned int''s, so we test bits 0-15
{
if ( (unsigned short) pow( 2, i ) & h->width )
wbits++;
if ( (unsigned short) pow( 2, i ) & h->height )
hbits++;
}
// Figure out coordinates for those images with two arrays of coordinates
// We will cut the image along these coordinates, and later, we will draw the
// pieces along these coordinates so it looks like its still one image
unsigned short* x_vertices;
unsigned short* y_vertices;
int xci = 1; // x-coordinate iterator; we start at one because already know x_vertices[0] is 0
int yci = 1; // same
x_vertices = new unsigned short[wbits + 1]; // we need one more coordinate so every piece has a right and left coordinate
y_vertices = new unsigned short[hbits + 1];
x_vertices[0] = 0; // don''t need to calculate the top left of the image ;]
y_vertices[0] = 0;
for ( int i = 15; i >= 0; i-- ) // calculate the rest
{
if ( (unsigned short) pow( 2, i ) & h->width )
{
x_vertices[xci] = (unsigned short) pow( 2, i ) + x_vertices[xci - 1];
xci++;
}
if ( (unsigned short) pow( 2, i ) & h->height )
{
y_vertices[yci] = (unsigned short) pow( 2, i ) + y_vertices[yci - 1];
yci++;
}
}
// Create a matrix of textures to hold the pieces of the bitmap
// Load each texture into the GL
// Create a display list for each texture
Texture* t_matrix = new Texture[wbits * hbits];
unsigned int listbase;
listbase = glGenLists( wbits * hbits + 1 ); // wbits * hbits lists to draw each piece
// one more list to draw all the other lists
for ( int row = 0; row < hbits; row++ ) // for each row (y)
{
for ( int col = 0; col < wbits; col++ ) // for each column (x)
{
// Copy a piece of the image
h->Blit( x_vertices[col], // top left corner
y_vertices[row], // bottom right corner
x_vertices[col + 1] - x_vertices[col], // width
y_vertices[row + 1] - y_vertices[row], // height
&t_matrix[row*wbits + col] ); // pointer to the texture
// Load the texture into the GL
glGenTextures( 1, &t_matrix[row*wbits + col].id );
glBindTexture( GL_TEXTURE_2D, t_matrix[row*wbits + col].id );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
/**** PROGRAM CRASHES HERE!!! AHHHH!!! ****/
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, t_matrix[row*wbits + col].width, t_matrix[row*wbits + col].height, 0, GL_RGB, GL_UNSIGNED_BYTE, t_matrix[row*wbits + col].data );
delete [] t_matrix[row*wbits + col].data; // don''t need this anymore
// Create a display list with the new texture
glNewList( listbase + (row*wbits + col) + 1, GL_COMPILE );
glBindTexture( GL_TEXTURE_2D, t_matrix[row*wbits + col].id );
glBegin( GL_QUADS );
glTexCoord2f( 0.0f, 1.0f );
glVertex2i( x_vertices[col], y_vertices[row] );
glTexCoord2f( 0.0f, 0.0f );
glVertex2i( x_vertices[col], y_vertices[row + 1] );
glTexCoord2f( 1.0f, 0.0f );
glVertex2i( x_vertices[col + 1], y_vertices[row + 1] );
glTexCoord2f( 1.0f, 1.0f );
glVertex2i( x_vertices[col + 1], y_vertices[row] );
glEnd();
glEndList();
}
}
// Now create one big list that calls all the other lists
glNewList( listbase, GL_COMPILE );
for ( int i = 0; i < wbits * hbits; i++ )
glCallList( listbase + i + 1 );
glEndList();
h->id = listbase;
// Clean up
delete [] h->data;
delete [] x_vertices;
delete [] y_vertices;
delete [] t_matrix; // VIDEO MEMORY LEAK HERE!!! FIX IT!!!
game->errorlog->Log( "GraphicsDataManager", "Bitmap \"" + h->name + "\" loaded successfully", 1 );
return true;
}
Also of interest is the Texture::Blit() method, which I use to copy a section of the image.
void Texture::Blit( unsigned short xc, unsigned short yc, unsigned short size_x, unsigned short size_y, Texture* image )
{
// Error check parameters
if ( xc >= width || // coordinates must be inside the image
yc >= height ||
size_x == 0 || // we must blit _some_ image data
size_y == 0 ||
xc + size_x > width || // the size of the image cannot be outside the image
yc + size_y > height ||
image == NULL ) // we must not have already cleared data
return;
// Count the blit number for the image name
// Each image must have a unique name
// This is not important now, but eventually, these images will be part
// of the linked list in the GraphicsDataManager to prevent memory leaks
static char blit_x0 = ''0'';
static char blit_0x = ''0'';
blit_0x++;
if ( blit_0x == ''9'' + 1 ) // overflow, count up
{
blit_x0++;
blit_0x = ''0'';
}
// Prepare the image
image->Release(); // clean out anything already in there
image->name = name + blit_x0 + blit_0x;
image->width = size_x;
image->height = size_y;
image->data = new unsigned char[image->width * image->height * 3];
// Copy that section
for ( int y = yc; y < yc + size_y; y++ )
{
for ( int x = xc; x < xc + size_x; x++ )
{
image->data[y*image->width*3 + x*3 + 0] = data[(yc+y)*width*3 + (xc+x)*3 + 0];
image->data[y*image->width*3 + x*3 + 1] = data[(yc+y)*width*3 + (xc+x)*3 + 1];
image->data[y*image->width*3 + x*3 + 2] = data[(yc+y)*width*3 + (xc+x)*3 + 2];
}
}
}
If anyone has any suggestions on how I can make this work, I would _really_ appreciate it. Thanks in advance for your help.