the Fiddler's picture

New text renderer

Project:The Open Toolkit library
Version:0.9.x-dev
Component:Code
Category:task
Priority:normal
Assigned:the Fiddler
Status:closed
Description

I have just committed a WIP implementation of a new text renderer to the text branch. Improvements:

  1. Simpler to use.
  2. More modular. It is now possible to have different GL1/GL2/GL3-level output or text layout providers. It is also possible to specify your own implementations (for example if you wish to integrate this into your engine).
  3. More accurate rendering (better grid fitting).
  4. Improved speed and reduced GC pressure through caching.
  5. Lower texture memory consumption.

The new implementation is based on GdiPMeasureCharacterRanges, which provides better control over text layout, when compared to MeasureString (the old implementation). Text is now cached on two levels, rasterized glyphs (loaded in texture memory) and laid out text. The public API looks like this:

// No TextureFonts, TextHandles or anything.
TextPrinter text = new TextPrinter();
[...]
text.Begin();
text.Print("Hello, World!", font);
text.Print(fps.ToString(), font, TextPrinterOptions.NoCache);
text.End();
[...]
TextExtents extents = text.Measure("ABC", font);
RectangleF bbox = extents.BoundingBox;
RectangleF char_pos = extents[1];  // Returns the position of "B" - useful for e.g. drawing a caret or selecting text.

The implementation in SVN is still under heavy development. Caveats:

  • (Fixed) Single texture sheet limitation remains. This is proving more difficult than expected.
  • (Fixed) Sometimes binds the wrong texture for glyphs (funky results!)
  • Trying to draw / measure uncached strings with newlines will cause GC pressure. This is the unfortunate result of a bug in Mono's GdiPMeasureCharacterRanges implementation. Fortunately, this is unlikely to cause problems in real life (you don't usually create dynamic text with newlines in a tight loop).

Anyone still interested despite these problems, you can test the code from the text branch. Expect screenshots shortly.

Edit: Clarified GC-pressure caveat.


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
Mincus's picture

#31

Looks very nice.

With the background colour, would it possible to specify an assumptive colour for it?
when rendering text, most coders will know the background colour they'll be rendering the text on to, so maybe adding that as an option would be a possiblity?

Good work though!

the Fiddler's picture

#32

With the background colour, would it possible to specify an assumptive colour for it?
Not really:

  • This would burn through texture memory like no tomorrow (each bg color & glyph combination would have to be stored separately).
  • It still wouldn't help, since it can't cope with gradients, textures, etc as backgrounds.

Inertia is right actually: this needs access to the framebuffer content, plus an implementation that can blend glyphs on arbitrary backgrounds. If I remember correctly, WPF can do this on the GPU if it supports SM3+, otherwise it falls back to software (or ignores gamma, not sure which of the two).

I think the current implementation is the best we can do from an quality / effort standpoint (which is still a lot better than most other similar libraries.)

Inertia's picture

#33

I think what Mincus meant was that text in games is usually not shown without a black 1 pixel border around the letters, or it's ontop of a layer of a single color. So the programmer could specify that the background where the text is used is always color #112233 and text color #ffffff. If the layer is opaque this should give ideal results, if it's translucent it'll still give good results. No need to examine the framebuffer.

The scroll bar at the side implies that the text is not baked into the "paper" texture below it, and the places where text is written has roughly the same color.

P.S: I was curious and a quick search turned out that the gamma (=g) corrected result (=a) for input value (=b) [0..255] can be obtained with this formula:

a = 255 * pow( b / 255, 1.0 / g );

g of 1.0 results in uncorrected linear distribution.
g of 1.4 is the recommended default.

the Fiddler's picture

#34

Yeah, I get that, but I still don't think this idea could work from a quality / performance standpoint. However, we can add a GL2-level implementation that uses the above formula - I think its fair for GL2-level hardware to get better quality.

As far as I can tell, the text on this image uses plain grayscale antialiasing and it looks pretty nice (a tad blurry, but that could be the jpg compression at work). Subpixel AA would look better, even without any gamma-correction.

Inertia's picture

#35

I agree, just thought this should not be missing in this topic. Sorry, don't have World of Warcraft and cannot find a better quality picture, but I think it served the purpose of showcasing an in-use scenario of fonts. Most certainly they're using greyscale, low system requirements is something all blizzard games share.

objarni's picture

#36

Is it possible to render text with the quality seen in Inertias picture example using OpenTK, on top of textured areas of the screen as seen in the picutre?

Inertia's picture

#37

No, you can only print "Hello, World!" in different sizes. *rolleyes*

objarni's picture

#38

Inertia:

What do you mean by your remark Inertia? Could you detail a little?

the Fiddler's picture

#39

Yes, OpenTK can print text on top of textured surfaces. Compared to the screenshot above, the new renderer will display text with better contrast.

objarni's picture

#40

Thanks fiddler. If it can produce better result than the screenshot, it is quite alright by my standards :)