OpenGL ES Loading Textures

Apprentice
Posts: 12
Joined: 2009.04
Post: #1
I have a major problem loading png textures on my OpenGL ES code. I need some help doing so, because this will drive me nuts.

here is the code with which I am loading textures:
Code:
- (void)loadTexture:(NSString *)name intoLocation:(GLuint)location {
    
    CGImageRef textureImage = [UIImage imageNamed:name].CGImage;
    if (textureImage == nil) {
        NSLog(@"Failed to load texture image");
        return;
    }
    
    NSInteger texWidth = CGImageGetWidth(textureImage);
    NSInteger texHeight = CGImageGetHeight(textureImage);
    
    GLubyte *textureData = (GLubyte *)malloc(texWidth * texHeight * 4);
    
    CGContextRef textureContext = CGBitmapContextCreate(textureData,
                                                        texWidth, texHeight,
                                                        8, texWidth * 4,
                                                        CGImageGetColorSpace(textureImage),
                                                        kCGImageAlphaPremultipliedLast);
    
    // Rotate the image
    CGContextTranslateCTM(textureContext, 0, texHeight);
    CGContextScaleCTM(textureContext, 1.0, -1.0);
    
    CGContextDrawImage(textureContext, CGRectMake(0.0, 0.0, (float)texWidth, (float)texHeight), textureImage);
    CGContextRelease(textureContext);
    
    glBindTexture(GL_TEXTURE_2D, location);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureData);
    
    free(textureData);
    
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}

However, this code works with some png files, but not with others! I am enclosing my entire project for you to see. In this project, I have created a 2D mesh, and I am displaying a texture with it.

I am using Cocos2D for the setup, but this has nothing to do with the rest of the OpenGL code, as I am doing all of the drawing in OpenGL ES. In the project, if you try running it with "romo.png", "bluetex.png", "combinedtextures.png" as the active image, the image will be displayed correctly. Otherwise, it will not be displayed. I have made tests with the Texture2D classes to no effect.

Please, download the project (...which is for your convenience. No essential or copyrighted code is written in there) and tackle with the images to see what's happening.

The project is The project is here

Can anyone help me, or at least point me somewhere where I can see how to load PNG files correctly? This is driving me nuts!
Quote this message in a reply
Sage
Posts: 1,232
Joined: 2002.10
Post: #2
Add one line of debug code:

Code:
...
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureData);

printf("Got error %04x for TexImage %d x %d\n", glGetError(), texWidth, texHeight);
...

You'll see that the 256x256 textures work, and the others like 480x320 produce GL_INVALID_VALUE.
It's because the hardware only supports power-of-two textures.
Quote this message in a reply
Apprentice
Posts: 12
Joined: 2009.04
Post: #3
Yes, you are indeed right. I had forgotten that very important detail, and that cost me frustration, and your time, sorry. Thanks a lot for taking the time to look at it.

Can you show me how can I bypass that limitation?

I would really like to be able to load 2D textures as background images that are 480x360 in size... (the size of the iPhone screen)
Quote this message in a reply
Sage
Posts: 1,232
Joined: 2002.10
Post: #4
You have three options:

1) Resize the image to a power of two. If you resize down, you lose quality. Resizing up uses more memory.
2) Keep the image the same size, but put it inside of a larger power-of-two texture. You can put other things in the extra padding space (i.e. make a texture atlas.)
3) Keep the image the same size, but split it into several smaller textures which are all power of two. I.e. 320 = 256+64, 480 = 256+128+64+32.

1) is easiest. 2) requires data changes, to use the proper texture coordinates inset in the texture. 3) requires code changes, to draw multiple quads.
Quote this message in a reply
Moderator
Posts: 3,577
Joined: 2003.06
Post: #5
Personally, I prefer to use option 3 for uncompressed backgrounds (i.e. not when using pvrtc), since it uses less memory at the expense of a few extra calls, while memory is at a premium on iPhone.

Option 1 is definitely very easy. Here's an example of how you could do that:

Code:
GLuint        backgroundTexture;

- (void)loadBackground
{
    CGImageRef        image;
    CGContextRef    imageContext;
    GLubyte            *imageData;
    
    image = [UIImage imageNamed:@"Default.png"].CGImage;

    if(image)
    {
        imageData = (GLubyte *)malloc(512 * 512 * 4); // four channels is only useful for images with alpha, so better to use 3 instead
        imageContext = CGBitmapContextCreate(imageData, 512, 512, 8, 512 * 4, CGImageGetColorSpace(image), kCGImageAlphaPremultipliedLast);
        CGContextDrawImage(imageContext, CGRectMake(0.0, 0.0, 512.0f, 512.0f), image);
        CGContextRelease(imageContext);
        glGenTextures(1, &backgroundTexture);
        glBindTexture(GL_TEXTURE_2D, backgroundTexture);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 512, 512, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        free(imageData);
    }
}

And here's how you might use it:

Code:
- (void)drawView
{
    [EAGLContext setCurrentContext:context];

    glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
    glViewport(0, 0, backingWidth, backingHeight);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrthof(-1.0f, 1.0f, -1.5f, 1.5f, -1.0f, 1.0f);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    // draw background
    const GLfloat backgroundVertices[] = {
        -1.0f, -1.5f,
        1.0f,  -1.5f,
        -1.0f,  1.5f,
        1.0f,   1.5f,
    };
    const GLfloat backgroundTexCoords[] = {
        0.0f, 1.0f,
        1.0f, 1.0f,
        0.0f, 0.0f,
        1.0f,  0.0f,
    };

    glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    glEnableClientState(GL_VERTEX_ARRAY);
    glTexCoordPointer(2, GL_FLOAT, 0, squareTexCoords);
    glVertexPointer(2, GL_FLOAT, 0, backgroundVertices);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, backgroundTexture);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
    [context presentRenderbuffer:GL_RENDERBUFFER_OES];
}
Quote this message in a reply
Apprentice
Posts: 12
Joined: 2009.04
Post: #6
Thanks a lot for your answers. At first, I tried to resize the image. The result was what I expected. Since I will use this method only once per level, I think I will pass on the rest of the methods. I may use them on other projects, though.
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  Loading textures only when they're needed? daveh84 5 2,886 Dec 1, 2009 10:14 AM
Last Post: warmi
  OpenGL ES Textures Start White demonpants 5 7,023 Sep 3, 2009 06:40 AM
Last Post: Skorche
  [Problem] Loading image with OpenGL orthogonal view pixel-to-pixel Lexis 2 3,718 Aug 21, 2009 01:35 AM
Last Post: Lexis
  opengl es font textures? cool mr croc 0 2,389 Aug 17, 2009 04:52 AM
Last Post: cool mr croc
  OpenGL textures upside down? orbian 1 9,416 Mar 13, 2009 02:53 PM
Last Post: ThemsAllTook