Saturday, March 13, 2010

Higher Learning

One thing that I've come to realize about programming is that if you want to create better programs you need a better understanding of the languages, tools, and process you're using. Now, as great a tool as the internet can be in quickly finding information, I still have a soft spot for a solid book that covers a subject in depth and detail. Actually finding books of this caliber can be quite difficult, but when you find them they are truly a godsend. The two most recent additions to my programming library are OpenGL SuperBible Fourth Edition and The iPhone Developer's Cookbook: Building Applications with the iPhone 3.0 SDK 2nd Edition.


Both these books are monsters, weighing in at about 1,200 pages and 800 pages respectively. Diving into them, though, has been satisfying and relieving. It's nice to have solid reference material available vs. wading through google search listings.

Saturday, March 6, 2010

Pixel Fonts & Texturing

Typically, when you work in a new programming environment, the first program you write is "Hello World!". When writing a complex program, it is essential to display program specific information to the screen, while the program is running. A "Hello World!" program lays the groundwork to achieve this.

Now, as simple as displaying "Hello World" on the screen might seem, in an OpenGL ES environment on the iPhone, this is anything but. There is no built in font. There are no built in routines to handle pre-made fonts. Essentially, you have to do it yourself. That, or find some pre-made code, which I'm not the biggest fan of in this case. The reality is, custom font routines are a necessary evil when you work in a graphical environment. To do them right takes a considerable amount of effort, and the payoff seems very small. When you finally have a solid, working set of custom font routines, showing them off just doesn't really produce that wow factor. It's about as exciting as seeing "Hello World!" on the screen. From a production value standpoint, though, they definitely make a difference and to me it's the difference between an okay program and a excellent program.

Now, the end goal of a custom font routine is to have code that looks something like:
displayFont(customFont, x, y, "Hello World!")

This encapsulates our core functionality, which is to display a string of text, in a custom font at an (x,y) position on the screen. The way we do this is by stepping through each character of the text string and converting it into a code, typically ASCII. We use this code as an index into an array of glyph (character) images. We then display the glyph at the current (x,y) position and then advance x by the length of the glyph, so that the next glyph is displayed after the current one, instead of on top of it. Generally, this is the easy part.

The hard part is actually constructing the glyph image array. Since we convert each text string character into a code to use as an index into this array, we must ensure that these glyph images are ordered according to the codes we're using. ASCII codes are the typical choice here. To achieve this, we need to construct our glyph images in order and in such a way that we can write a routine to strip each glyph from our font image. The easiest way to do this is to construct the glyphs in a grid, one glyph per cell. The most important part here is that every grid cell should have the same dimensions. This uniformity is what allows our routine to quickly strip each glyph from the font image.

Okay, so now that we have a system in place, it's time for the lovely task of creating our font! This is a real time killer. I don't spend too much time making the first font. In fact, I usually only do numbers, and uppercase letters. The first font you build is really a place holder to ensure that your system actually works correctly. Having done this many times, in many languages, I opted to use the power of the internet to see if I couldn't find some decent, free pixel fonts. Remember, in order to use a pre-made pixel font, the glyphs need to be contained in a grid in ASCII code order, one glyph per cell, with cells of the same size. After wading through that monster list, I chose to start with font 159.


This font is a clean, simple, single color font with well suited dimensions for display on the iPhone. Not too big and not too small. 

With our font selected, now comes the challenge of loading the font image and accessing the pixel data so we can strip out the individual glyphs. I must say, this part of the process is a hellish nightmare. Here's what needs to happen:
  1. load font image
  2. access image pixel data
  3. properly enable OpenGL to handle texture mapping
  4. pass pixel data to OpenGL as a texture
  5. strip glyphs from texture
 In practice, all of these steps are specific to both the iPhone platform, and OpenGL

To load our image we use the UIImage imageNamed: method to save a CGImageRef of the resultant CGImage:
CGImageRef textureImage = [UIImage imageNamed:filename].CGImage;

Next we need to allocate a block of memory to place the image pixel data. This is the data we will pass to OpenGL for our texture. We use C's malloc() to allocate this block of bytes. The number of bytes we need to allocate is equal to textureWidth * textureHeight * bytesPerPixel. If you're working with 32bit RGBA textures, you've got 1byte per color component, or 4bytes per pixel.
GLubyte *textureData = (GLubyte *)malloc(textureWidth * textureHeight * bytesPerPixel);

With our memory allocated, we need to transfer our image to this memory block. This gives us direct access to the pixel data because we have direct access to the memory block that will contain that data. In order to transfer our image, we  need to create a Core Graphics context. I think of a context as a lens through which your source image passes and is translated into the correct digital information. We use CGBitmapContextCreate() to create our CGContextRef. We then transfer the image using CGContextDrawImage() with our CGContextRef. With our image transfered, we have no more need for the context, so we can release it via CGContextRelease().

Now that we have direct access to our image pixel data, we need to transfer it to OpenGL. Once transferred, OpenGL keeps it's copy of the image as a texture, which it can render with incredible speed through hardware acceleration. It must be noted that OpenGL ES only accepts textures with dimensions that are powers of 2 (1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024). The dimensions don't need to be the same size, but they MUST be powers of 2. Now, before we pass the texture, we need to create a name with which we can reference the texture later. In essence, we tell OpenGL that we want to draw this named texture, and with the name it knows which texture to draw. This name is really a unique number that we ask OpenGL to give us using glGenTextures(). After generating a texture name, we use glBindTexture() to tell OpenGL specifically which texture we'd like to work with. Finally, we use  glTexImage2D() to pass our image pixel data to that bound texture.

After passing the texture to OpenGL, we no longer need our copy of the image, so we use C's free() function to release the memory block containing it.

Once OpenGL has our texture, we can specify which region of the texture we want to display by passing the coordinates of that region using glTexCoordPointer(). Be aware that texture space extends from 0.0 to 1.0 along the UV axes. This means to translate from our original image pixel space to OpenGL texture space, we need to divide by the dimensions of our original image.
texture.u = pixel.x / image.width
texture.v = pixel.y / image.height

These UV coordinates are the coordinates that we need to pass to OpenGL, specifying the region of the texture we wish to display. 

Finally, this is how we strip the glyphs from the texture! We step through each cell of the grid in the texture and calculate the UV coordinates of the corners of each cell. We then store these coordinates into the glyph array. Since the glyphs are ordered in the texture itself, we are making ordered entries into the glyph array.

Alright, final step here. To actually display our texture regions OpenGL expects a few things. First, we need to enable texturing with glEnable(GL_TEXTURE_2D). Then we need to specify which texture we want to work with using glBindTexture(). Next, we need to tell OpenGL how we want it to scale our textures:
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

GL_LINEAR makes scaled textures blurry. We can use GL_NEAREST if we want blocky scaled textures. Next, we enable the texture coordinate array using glEnableClientState(GL_TEXTURE_COORD_ARRAY). This let's OpenGL know that you are going to pass it a set of UV coordinates. Then we tell OpenGL where the UV coordinates are stored using glTexCoordPointer(). Finally, we have OpenGL draw everything using glDrawArrays(). From here we just need to disable the texture coordinate array: glDisableClientState(GL_TEXTURE_COORD_ARRAY) and then disable texture mapping: glDisable(GL_TEXTURE_2D).

Alright! So, that covers the fundamentals of loading and displaying textures and using OpenGL to render a pixel font and as you can see it takes quite a bit of effort to achieve. It's taken me about a week, and a lot of frustrating days to get this up and running correctly on the iPhone, but I'm loving the results.