iDevGames Forums
opengl text/font drawing from CGContext - Printable Version

+- iDevGames Forums (http://www.idevgames.com/forums)
+-- Forum: Development Zone (/forum-3.html)
+--- Forum: Graphics & Audio Programming (/forum-9.html)
+--- Thread: opengl text/font drawing from CGContext (/thread-2085.html)



opengl text/font drawing from CGContext - fretmunky - Dec 2, 2008 12:16 PM

Hi, I've just registered on this forum as it has provided a lot of help in recent weeks (thanks!) but now I have a problem I've not discovered a solution to...

I'm trying to write a font class that draws text to a CGContext then uses it as an opengl texture. The following code is based on a texture class I wrote which does a similar thing (loads a UIImage, draws it to a CGContext and then uses the context pixel data for an opengl texture, the texture is actually drawn elsewhere however)... anyway... it works... but this doesn't.

When the following method is called a rectangle is displayed onscreen but it is completely black and thus there's no text to be seen. Is this method of rendering text using opengl a daft idea? Any ideas or help on this would be really helpful.

I know there are a few naughty bits in the following code, which i plan on sorting out when I know this is going to work - for example: integers width and height are not determined correctly.

Anywhoooo, the code...


Code:
void CFont::drawString( char *string, int posX, int posY )
{
    // Determine size of drawing area need to fit string (pixels)
    int width = (sizeof(string)/sizeof(char)) * size * 4;
    int height = size * 4;
    
    // Allocate memory for bitmap data (used by gl)
    glData = (GLubyte*)(malloc(width*height*4));
    
    // Create texture
    GLuint texture;
    glGenTextures( 1, &texture );
    
    int actualWidth = width;
    int actualHeight = height;
    bool sizeToFit = false;
    NSUInteger i;
    
    // Scale image width to next pot size        
    if( (actualWidth != 1) && (actualWidth & (actualWidth - 1)) )
    {
        i = 1;
        while( (sizeToFit ? 2 * i : i) < actualWidth )
            i *= 2;
        actualWidth = i;
    }
    
    // Scale image height to next pot size        
    if( (actualHeight != 1) && (actualHeight & (actualHeight - 1)) )
    {
        i = 1;
        while( (sizeToFit ? 2 * i : i) < actualHeight )
            i *= 2;
        actualHeight = i;
    }
    
    // Scale image down if now larger than 1024 (the maximum texture size)
    while( (actualWidth > 1024) || (actualHeight > 1024) )
    {
        actualWidth /= 2;
        actualHeight /= 2;
        width *= 0.5;
        height *= 0.5;
    }
    
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    
    // Create bitmap context to draw to
    context = CGBitmapContextCreate( glData, actualWidth, actualHeight, 8, 4*actualWidth, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big );
    
    CGColorSpaceRelease(colorSpace);

    // Set context properties
    CGContextSelectFont( context, name, size, kCGEncodingFontSpecific );
    CGContextSetCharacterSpacing( context, charSpacing );
    CGContextSetTextDrawingMode( context, kCGTextFillStroke );
    CGContextSetRGBFillColor( context, fillColour_red, fillColour_green, fillColour_blue, fillColour_alpha );
    CGContextSetRGBStrokeColor( context, strokeColour_red, strokeColour_green, strokeColour_blue, strokeColour_alpha );
    
    // Clear context and draw text to it
    CGContextClearRect( context, CGRectMake(0.0f, 0.0f, (CGFloat)actualWidth, (CGFloat)actualHeight) );
    CGContextShowTextAtPoint( context, 0, 0, string, sizeof(string)/sizeof(char) );
    
    // Release the context, we're done with it
    CGContextRelease( context );
    
    // Bind texture and define image data to render
    glBindTexture( GL_TEXTURE_2D, texture );
    glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, actualWidth, actualHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, glData );
    
    // Vertex coords for texture
    const GLfloat vertices[] = {
        posX, posY,
        posX+width, posY,
        posX, posY+height,
        posX+width, posY+height,
    };
    
    // Texture coords
    const GLshort textureCoords[] = {
        0, 1,
        1, 1,
        0, 0,
        1, 0,
    };
    
    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    glRotatef( -90.0f, 0.0f, 0.0f, 1.0f );
    glTranslatef( 0.0f, 0.0f, 0.0f );
    glOrthof( 0.0f, 480.0f, 0.0f, 320.0f, -1.0f, 1.0f );
    glMatrixMode( GL_MODELVIEW );
    
    // Set suitable OpenGL states and parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glEnable(GL_TEXTURE_2D);
    
    // Point at what we're going to draw
    glVertexPointer(2, GL_FLOAT, 0, vertices);
    glEnableClientState(GL_VERTEX_ARRAY);
    glTexCoordPointer(2, GL_SHORT, 0, textureCoords);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    
    // Draw it... pow!
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    
    // Reset OpenGL states
    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    glDisable(GL_TEXTURE_2D);
    
    // Clean up
    free(glData);
}


Any thoughts?... Cheers for your time,

Chris.


opengl text/font drawing from CGContext - fretmunky - Dec 3, 2008 12:44 PM

I've scrapped the above since I couldn't get it to work and opted for the more traditional texture-mapped bitmap font method. Just out of interest, is there no one who knows why the above didn't work? The above (if it worked) would give a bit more flexibility with drawing fonts than texture-mapped bitmap fonts, i.e. stroke and fill colours could be altered at runtime.


opengl text/font drawing from CGContext - Bachus - Dec 4, 2008 12:19 AM

Have you checked out Apple's CrashLanding iPhone sample code? It has a texture class that does exactly this.


opengl text/font drawing from CGContext - fretmunky - Dec 4, 2008 02:35 AM

Yep, your quite right, it does. I've looked at the CrashLanding example in the past but I guess in my haste, overlooked that particular method. Anyway thanks!

However, it seems the example doesn't approach it in the same way. The resulting texture, uses an alpha-only colour space, which will not provide the above mentioned functionality (changing stroke and fill colours).

I'm interested in the performance differences in using this approach vs. the texture-mapped bitmap one that I ended up going for. I'll likely do some tests with this when I have the time (which I don't at the moment) but if anyone else has any info on this, it'd be cool to hear. Also, what about localization? In particular, using Chinese characters?


opengl text/font drawing from CGContext - AnotherJake - Dec 4, 2008 07:54 AM

fretmunky Wrote:I'm interested in the performance differences in using this approach vs. the texture-mapped bitmap one that I ended up going for. I'll likely do some tests with this when I have the time (which I don't at the moment) but if anyone else has any info on this, it'd be cool to hear. Also, what about localization? In particular, using Chinese characters?

Performance will always be better when rendering one textured object vs. multiple textured objects to achieve the same effect (i.e. Apple's example in Texture2D will be faster than the equivalent bitmapped font approach, assuming you don't change the string). I wouldn't worry about it if you're just doing a few characters, but for larger strings, definitely shy away from bitmapped fonts -- unless you are changing the strings frequently. If you are changing the string frequently, like every second or less, it may be much better to do the bitmapped font approach, since there is some overhead in having to re-render the texture which may noticeably affect performance.

Typesetting and kerning is also a pain to take care of in a bitmapped font, if you need it, but is handled automatically in Texture2D.

Localization naturally works great with the Texture2D sample.


opengl text/font drawing from CGContext - fretmunky - Dec 4, 2008 09:12 AM

AnotherJake Wrote:Performance will always be better when rendering one textured object vs. multiple textured objects to achieve the same effect (i.e. Apple's example in Texture2D will be faster than the equivalent bitmapped font approach, assuming you don't change the string). I wouldn't worry about it if you're just doing a few characters, but for larger strings, definitely shy away from bitmapped fonts -- unless you are changing the strings frequently. If you are changing the string frequently, like every second or less, it may be much better to do the bitmapped font approach, since there is some overhead in having to re-render the texture which may noticeably affect performance.

Typesetting and kerning is also a pain to take care of in a bitmapped font, if you need it, but is handled automatically in Texture2D.

Localization naturally works great with the Texture2D sample.


Thanks for the info. Considering what you've said, it seems a combination of the two approaches would be the most performance friendly. I.e. the context approach for strings which generally remain the same and the bitmapped approach for strings which are often changing (timers, counters, etc.).

Thanks again,

Chris.


opengl text/font drawing from CGContext - AnotherJake - Dec 4, 2008 09:33 AM

Yes, unfortunately there is no one-size-fits-all solution to text rendering in OpenGL. Those are the two ends of the spectrum. There are all kinds of different ways to approach it.

On the context end you could replace Apple's Quartz with Freetype for a more cross-platform compatible solution instead. On Mac, OneSadCookie came up with an incredibly simple little way to render text from an NSView which is pretty neat too (don't have a link on-hand).

You could render bitmap fonts yourself in software into your own "context" and upload that as a single texture. I've done that too.

Then you can even split up multiple lines of text into what I call text "panels", consisting of either one line or more at a time. I've done that for some scrollable text.

Generally though, I either choose good-ol' bitmap fonts on quads (four point triangle strips nowadays) often for numbers which change frequently or for stuff I'd like some graphical effects on, done in Photoshop perhaps, or just Texture2D for convenience or nicer looking typeset text. Use the best technique for the situation.


opengl text/font drawing from CGContext - fretmunky - Dec 4, 2008 11:24 AM

Indeed. Cross platformability (lol) isn't really an issue for me at the moment as I'm working on iPhone stuff but I'll keep in mind using FreeType as opposed to Quartz for my own projects in the future.

Could you elaborate on those "text panels" for scrolling text? That could be VERY handy.


opengl text/font drawing from CGContext - AnotherJake - Dec 4, 2008 12:37 PM

Yeah, I haven't used FreeType in a while myself because I haven't been interested in cross-platform lately. To be honest though, I don't know how to load a custom font using Quartz, but I know that is easy to do with FreeType, so there may or may not be an advantage to FreeType on that point -- I don't know.

On the text panels: It's too tricky to explain in detail, but roughly speaking I just render one or a few lines per panel as the panel "scrolls" into view. It's like one quad per each line of text instead of one quad for each character. That way I don't have the extra overhead of either rendering the entire text view every frame or rendering lots of quads (one for each character), plus typesetting looks pretty good. You have to have a way to either clip each panel to your subview, or you have to calc the proper size of the panel (just a quad) and its corresponding texture coordinates so it *appears* like it's being clipped as it moves onto/off of the view.

I imagine one could also do it by translating texture coordinates on only one texture and quad and only updating part of the texture as it scrolls into view. I'm going to try that next time I need it because I think that might be a better technique, although I haven't fully envisioned the implementation details.


opengl text/font drawing from CGContext - OneSadCookie - Dec 4, 2008 12:42 PM

Using custom fonts with Quartz is a matter of an Info.plist key pointing to the font, then it's available through the normal APIs in the normal way.

The big disadvantage to avoiding Quartz is if you want to support Arabic, Thai, or any other complicated languages.


opengl text/font drawing from CGContext - AnotherJake - Dec 4, 2008 01:50 PM

OneSadCookie Wrote:Using custom fonts with Quartz is a matter of an Info.plist key pointing to the font, then it's available through the normal APIs in the normal way.
Awesome, I'm going to have to try that when I get a chance!

Quote:The big disadvantage to avoiding Quartz is if you want to support Arabic, Thai, or any other complicated languages.

That, and a minor disadvantage is that Apple has (or at least had) some patented font rendering stuff which wasn't available to use in FreeType, legally, without a license. I don't know if that situation has changed or not though, as I seem to recall at least one particular patent was expiring around this time.

[edit] Oh yeah, and you have to distribute FreeType with your application too, whereas Quartz is already on the machine.


opengl text/font drawing from CGContext - fretmunky - Dec 7, 2008 11:29 AM

Cheers for the info guys! It's been a big help Rasp