MULTIMEDIA

# iPhone 3D Programming : Drawing an FPS Counter (part 1) - Generating a Glyphs Texture with Python

8/25/2011 3:15:28 PM
##### Rather than demonstrating text rendering with yet another goofy toy application, let’s do something useful for a change. Overlaying a frames-per-second counter in one corner of the iPhone screen provides a quick and easy way of evaluating graphics performance; see Figure 1.Figure 1. FPS counterBefore writing any application code, you’d need to generate an image that contains bitmaps for the numbers zero through nine, as depicted in Figure 2. (Don’t bother trying to create this yet; you’ll see a way to automate this shortly.)Figure 2. Numerals in a 128x32 luminance textureYou probably already guessed that you need to store off the bounding box of each glyph in order to compute the appropriate texture coordinates. Thinking about this a bit more, you’ll realize a mere bounding box is not enough. When you’re writing a sentence on ruled paper, some parts of letters extend below the baseline, like the descender of the lowercase p. And, in the case of the rather artsy font shown in Figure 3, the type designer wants the 9 numeral to be vertically offset from the other letters. Further complicating matters is the fact that the bounding boxes of glyphs can overlap in the destination image. In Figure 3, observe how the descender of the letter p extends right below the letter i.Figure 3. Simple text layout with baselineIt turns out that associating a specific set of glyph metrics with each character supplies enough information to achieve the simple text layout shown in Figure 3. A popular naming convention for these metrics is described in Figure 4; in this diagram, the origin represents the current pen position.Figure 4. Glyph metrics: bearing and advance vectors; width and height lengthsTo summarize, the four glyph metrics are as follows:Bearing vector2D vector describing the offset from the pen position.Advance vector2D vector describing how to advance the pen to the next position after rendering the current glyph. The y component is always zero for Latin-based alphabets.WidthThe horizontal length of the glyph.HeightThe vertical length of the glyph.Using these metrics, Example 1 the pseudocode for a simple text layout algorithm.Example 1. Simple text layout algorithm`void RenderText(string s, vec2 p){ for each character c in s { metrics m = GlyphTable[c].Metrics vec2 topLeft = GlyphTable[c].Position box source = box(topLeft, m.Width, m.Height) box dest = box(p + m.BearingVector, m.Width, m.Height) Blit(source, dest) p += m.AdvanceVector }}` #### 1. Generating a Glyphs Texture with Python Before writing any application code, we need to choose a way of generating a glyphs texture and a set of metrics for each glyph.Leveraging Quartz is perhaps the most obvious way of generating a glyphs texture . This can be done at runtime when your application first starts up. This might slow down your startup time by a tiny amount, but it has the benefit of shrinking the size of the application bundle.My preference is to generate the glyphs texture as a build step, mostly because it simplifies my application code. Build steps take place in Xcode rather than the iPhone execution environment, which brings a much richer tool set to the table. This is a perfect use case for a scripting language, and Python comes to mind first.Note:There are many ways of generating a glyphs texture; here I’m giving an overview of my favorite. Take it only as a high-level example.Given that we’re using Python in a build step, we need to find some useful Python modules for image manipulation and image generation. At the time of this writing, the Python Imaging Library (PIL) is the most popular imaging manipulation module, and it provides excellent support for manipulating PNG images at a low level. However, it’s not quite enough on its own because it doesn’t provide direct access to the glyph metrics that we need. Another popular library is Cairo, which has a well-maintained Python binding called pycairo. Cairo is robust and fast, and it’s used as the rendering backend in Firefox, Mono, and GTK. So, let’s go with PIL (http://www.pythonware.com/products/pil/) and pycairo (http://www.cairographics.org/pycairo/).## Installing Python Modules The copy of Python that’s installed on Mac OS X won’t have these modules installed by default, so you’ll have to do a little bit of prep to install them.First, install the Python Imaging Library. Go here to download the source: http://www.pythonware.com/products/pil/. At the time of this writing, the 1.1.7 version of PIL was the most recent and worked well. Make sure you download the version for Python 2.6 to ensure that the script will work. Extract the tarball (you can double-click it to extract it), open the Terminal, and cd to the top-level directory of the source code distribution. Next, execute the following command:`sudo python setup.py install` Next, install pycairo. Download the source from here: http://www.cairographics.org/ (the script was tested with version 1.8.8). Extract the tarball, open the Terminal, and cd to the top-level directory of the source code distribution. Next, execute the following commands:`./configure \ --prefix=/System/Library/Frameworks/Python.framework/Versions/2.6makesudo make install` You’ll also need to install Cairo itself since pycairo is only a thin wrapper; build instructions and downloads are available from http://www.cairographics.org/download/.Rather than packaging the glyphs texture as a PNG or PVR file, let’s serialize the data to a C header file. Since it’s a single-channel texture, the amount of data is relatively small. The header file will also provide a convenient place to store the glyph metrics. We’ll simply have our Python script spawn PVRTexTool for generating the header file from the image. We’ll still generate a PNG file for preview purposes, but we won’t include it in the application bundle. See Example 7-2 for the complete Python script that generates Figure 2.Example 2. CreateNumerals.py`import cairoimport osfrom PIL import Image# Create a Cairo image surface:imagesize = (256,32)surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, *imagesize) cr = cairo.Context(surface)padding = 3# Choose a font (look in /Library/Fonts) and set up the transforms.cr.select_font_face("Apple Chancery", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD) cr.set_font_size(32)cr.set_source_rgb(1,1,1)# Create a string for appending the glyph metrics to the texture file:glyphs = '''struct GlyphPosition { int X; int Y;};\nstruct GlyphMetrics { int XBearing; int YBearing; int Width; int Height; int XAdvance; int YAdvance;};\nstruct Glyph { GlyphPosition Position; GlyphMetrics Metrics;};\nstatic const Glyph NumeralGlyphs[] = {\n'''# Render glyphs '0' through '9' and write out their extents:x, y = 0, 0 for character in '0123456789': extents = cr.text_extents(character) x_bearing, y_bearing, width, height, x_advance, y_advance = extents glyphs += ' {{ %d, %d }, ' % (x, y) glyphs += '{ %d, %d, %d, %d, %d, %d }},\n' % extents cr.save() cr.translate(x, -y_bearing) cr.text_path(character) cr.fill() cr.restore() x += width + padding glyphs += '};\n'# Extract the alpha channel and open it up for a quick preview:surface.write_to_png("NumeralsTexture.png") image = Image.open("NumeralsTexture.png") image.load()image.split()[3].save("NumeralsTexture.png") os.system("open NumeralsTexture.png")# Serialize the image data to a C header file:os.system('PVRTexTool -h -yflip1 -fOGL8 -iNumeralsTexture.png') # Write to the header file: headerFile = open('NumeralsTexture.h', 'a') headerFile.write(glyphs)headerFile.close() ` Note:For this to work, you must either put the location of PVRTexTool into your shell’s PATH environment variable or copy PVRTexTool into one of your PATH entries, such as /usr/local/bin. If you’ve extracted the Khronos SDK into your current directory, you could copy the file and mark it executable with these commands:`cd SDKPackage/Utilities/PVRTexTool/PVRTexToolCL/MacOSsudo cp PVRTexTool /usr/local/binsudo chmod +x /usr/local/bin/PVRTexToolcd -`
 Other

 Top 10
 REVIEW
- First look: Apple Watch

- 3 Tips for Maintaining Your Cell Phone Battery (part 1)

- 3 Tips for Maintaining Your Cell Phone Battery (part 2)