Wednesday, January 30, 2013

Kerning, Wrapping and Text Alignment

In my current project, I'm using the FTGL library for text rendering which does the job reasonably well, for Android I'm using a FTGL port called FTGLES (Ideally at some point I'd just like to be using just one of these.)

FTGL is very simple to get working but it doesn't support text alignment and text wrapping to the extent that I'd like. Therefore I've spent sometime modifying it a little and rewriting the central render loop (making it a little more efficient as a nice bonus too). Here's how everything looks now it's finished:

Horizontal Alignment

I want to be able to align my text to the left, right or center on the horizontal like so:

By default FTGL doesn't have support for right alignment. But it's pretty easy - just a case of getting the pixel width of the text and subtracting it from the x position. It's a little harder when text wrapping is involved.

Vertical Alignment

I want to be able to align my text top, bottom, center on the horizontal like so:

This is a little trickier because to do this well, you need to know where the baseline is (say if your were writing on lined paper, then the baseline would be one of those lines.) This wasn't easy to get out of FTGL so I worked around it.

The code supports any combination of horizontal and vertical alignments.

Consistent Baseline

If I render three pieces of text at the same Y position, I'd like it to appear as if each piece of text falls on the same baseline. Like so:

Text Wrapping

I want to be able to wrap paragraphs of text according to a maximum width. This text wrapping also needs to support the above alignment code. This picture uses a different font.

This shows only 3 of the possible wrapping alignment permutations but they're all supported. The text can also be scaled arbitrarily and everything still works fine.

I'm happy to stop working on this now. Though there are other features I could add: markup for colouring specific words in a text string, making FTGL go through my engine's pipeline like everything else, justified alignment, I suspect I've probably broken unicode support so testing that and fixing it would be good!

The hacked up code is available here:

Thursday, January 17, 2013

FTGL: Bad Code smell

I'm hacking around the internals of FTGL an OpenGL font rendering engine, it's pretty nice to use but it's not structured well underneath. It uses the pimpl idiom which is fine (though for games isn't ideal) but it's managed to use both composition and inheritance to do the same thing! It needed only to pick one. Every class has the class itself and then an additional class that has the implementation - so there are double the number of classes straight of the bat class1 and class1_implementation.

Here's an example: TextureFont inherits from a Font base class. When you construct a texture_font in the constructor a texture_font_implementation object is created which inherits from a font_implementation base class, this is then passed up and stored as a member of the Font class. It's ugly and inefficient, pimpl speeds up compile time but FTGL is a tiny library that can be statically built there's no good reason to use it.

The code smell in the blog title isn't anything to do with though. I recogonise this poor code as I do fallen prey it to more than once!

        /* Allow our internal subclasses to access the private constructor */
        friend class FTBitmapGlyph;
        friend class FTBufferGlyph;
        friend class FTExtrudeGlyph;
        friend class FTOutlineGlyph;
        friend class FTPixmapGlyph;
        friend class FTPolygonGlyph;
        friend class FTTextureGlyph;

The above code is in FTGlyph (and it wouldn't surprise me if the exact same code was all over the place). Each time you add a specialized new font you're going to have to remember to update this list and probably several other places in the code. It makes the code harder to extend and maintain. That's the smell, when you need to add something it should be in one place and one place only, no lists like the above snippet, there's always a cleaner way to do it.

In this case just make the constructor public, there's no strong need for it to be private. I think the use case for the friend keyword is extremely rare in itself.

Thursday, January 03, 2013

If you can't merge it, don't commit it

I was reading Behind Enemy Lines a blog post by about how Google develops Apple iOS apps and this section jumped out at me:

  • If you can’t merge it, you can’t use it. Ever solved a merge conflict on a xib file? Me neither. So xib’s weren’t allowed. Which suits me - I never liked xibs, story boards, all that junk.
  • Same with merge conflicts on .pbxproj files - these files weren’t checked into source control. Instead, there’s a Google open-source tool called GYP that generates your Xcode project from a JSON recipe and recursively searching the folders for source files. Worked pretty well, except that GYP is poorly documented and not able to perform certain project configurations (eg per-file -fno-objc-arc).

This seems to make a create deal of sense to me but it's the first time I've come across the idea explicitly stated. If it can't be merged, it shouldn't be in source control. So what do you do with those binary blobs that are required by your build process - the answer suggested is to generate the binary blobs from something that can be merged. This sounds like an excellent clean way of working.

If I reflect on some of my own my own source control repos, I probably have a few things in there that would benefit from being generated.

Of course for game development this needs balancing with practicality, do we want to have textures in markup? It would ensure we can always produce the required texture but the file sizes would explode! Source files for textures are usually PSD and have mutiple layers and history, even as a binary they can easily become 100 of megabytes in size. I know a number of studios have the binary game assets in a seperate source control program which might be the answer.

Game assets definitely benefit from the ability to roll back to earlier versions but trying to force it into a text-based format is usually worthless, a 3d artist isn't going to be able to merge changes to a Maya file from another artist in a standard merge tool. Though it there was more graphical merge tool, I see no reason why it couldn't work.