
Text rendering on Mono - investigate GDI+ alternatives
Posted Wednesday, 3 June, 2009 - 18:31 by the Fiddler| Project: | The Open Toolkit library |
| Version: | 0.9.x-dev |
| Component: | Code |
| Category: | bug report |
| Priority: | normal |
| Assigned: | the Fiddler |
| Status: | won't fix |
OpenTK relies on System.Drawing (GDI+) to render glyphs and lay them out to form text. Unfortunately, Mono's implementation of GDI+ suffers from several bugs: it cannot handle newline characters or word-wrapping.
We have worked around the first issue by splitting text on newline characters. This allows text rendering to work on Mono, but puts pressure on the GC. Unfortunately, there is no workaround for the second issue.
There are two solutions to this issue:
- Wait for Mono to finish it's new Pango-based text renderer, which will (hopefully) fix the bugs. This won't likely appear before Mono 2.8 (scheduled for December 2009 or somewhere near to that).
- Implement our own Mono.Pango-based IGlyphRasterizer.
The internal IGlyphRasterizer interface (OpenTK.Utilities) defines the methods for the rasterizer. There are two main methods in IGlyphRasterizer:
MeasureText, which returns a collection ofTextExtentinstances describing the layout of the text (one TextExtent per character).Rasterize, which rasterizes a glyph into aSystem.Drawing.Bitmap
We need to find how these methods can be implemented with Pango/Mono.Pango. As far as I can see, we need three things:
- Create a Pango context. I think we need to create a Cairo or GTK# context first.
- Map the desired
System.Drawing.Fontto aPango.Font. - Perform text layout and glyph rasterization using either high- or low-level Pango methods.
Once these are implemented, we can simply hook the new PangoGlyphRasterizer into the existing engine and use it when OpenTK.Configuration.RunningOnMono is true.
Any help with tasks 1 - 3 will be appreciated!
You can download Mono.Pango from mono-project.com (it is in the GTK# installer). It should come pre-installed on Ubuntu.


Comments
#1
I have been reading the Pango docs and it seems pretty straightforward what needs to be done. I'll talk in terms of the C API, I haven't checked out the C# API, but it shouldn't be too difficult to map the functions.
1. As you said, a Pango context needs to be created. This can be done with
pango_context_new().2. Well, I don't know how
System.Drawing.Fontworks, but to create a font in Pango we can usepango_font_description_from_string()which creates a new FontDescription from the string describing the font. I think the information necessary can be obtained from theSystem.Drawing.Fontfields, but I haven't looked at it more closely.3. To get the measures from the text, I think it's easier to use the high-level interface of Pango. A new Layout can be created with
pango_layout_new(), passing the previously created context. Then it needs to be setup with the font description obtained ealier and the text to be layouted.To get each glyph, we need to iterate by ever line in the layout. We call
pango_layout_get_iter()and get a PangoLayoutIter that can be used for iterating. I didn't quite understand what a 'run' means in Pango (the docs are quite poor) but I think it's what we need to use to iterate by all the glyphs in the iterator (it says: "(...) it is simply an alternate name for PangoGlyphItem.).We get each 'run' by calling
pango_layout_iter_get_run_readonly(). There is also another approach to this, by first iterating through the lines and then getting each glyph in a line, but if I understood correctly, this 'run' stuff lets us iterate only once by everything in every line and returns NULL if we got to the end of a line.Getting the information for each glyph is then a matter of calling the corresponding functions in Pango. I don't know what info OpenTK needs, so if someone can check the functions, and see if all the stuff is there (http://library.gnome.org/devel/pango/stable/pango-Glyph-Storage.html#Pan...).
Regarding the rendering, what backend is more interesting to use to render the glyphs? The Pango docs mention different alternatives we can choose, like using the Cairo library, FreeType 2 or Xft.
#2
Thanks for the explanation. I have managed to implement points 1 and 2 using Mono.Pango and Mono.Cairo and I'm reading the docs for LayoutIter for point 3.
I have attached a (very) small project with the implementation so far. It relies on GTK#, which can be found on mono-project.com (it comes preinstalled on Ubuntu).
Edit: We'll probably use Cairo for font rendering, if only because the API is very simple.
#3
The following code implements point 3. Unfortunately, the GetCharExtents signature uses pass-by-value semantics that don't allow this to work. Why can't life be simple for once?
#4
And success!
The final step is to figure out how to upload the contents of a Cairo surface onto a texture.
#5
I wasn't really understanding what a run was, so I went to IRC. This was the answer:
owen_> a run is a consecutive sequence of glyphs in the same font, with the same rendering attributes (color, underline, etc), and in the same direction (left-to-right, right-to-left)
I booted my Ubuntu VM and tested your code and it worked fine. Now regarding your last post, did you hit a binding bug? From what I can see, the parameters should be 'out' or 'ref', right?
#6
Indeed, GetCharExtents should actually be "
out Rectangle". I checked the Mono.Pango source and it seems this part is not finished yet. I've filed a bug at https://bugzilla.novell.com/show_bug.cgi?id=510105, but fortunately the issue was easy to work around using the code from my last post.The final issue is also easy to solve: you can get the raw data from a cairo ImageSurface using the DataPtr property.
I must admit that I'm very impressed with the Cairo and Pango APIs. They are both clean and intuitive, unlike say GDI+. Using Pango.TextLayout, I've managed to recreate weeks worth of GDI/GDI+ research, in just a couple of hours.
Thank you for your help, triton. I am going to put all this together and add it to OpenTK over the next few days. If anything else comes up, I'll make a post.
Edit: regarding text runs, they are a step above the TextPrinter. Conceptually, every call to TextPrinter.Print renders a single text run. I'm working on a "FormattedText" class that is composed of multiple text runs, but I don't know if that will ever become part of OpenTK (I'll upload it to the database when it's ready).
Things would be very different if .Net text-rendering wasn't so hopelessly broken...
#7
I have copied and simplified the necessary Cairo and Pango binaries, which means the test project no longer relies on Mono.Pango and Mono.Cairo being installed. This was done mainly to avoid introducing new build dependencies.
I'll push the code to OpenTK for testing, once I get glyph rasterization up and running.
#8
The TextPrinter project will be spun off from the main OpenTK distribution. Maintainers wanted.
#9
Couldn't it be part of an OpenTK.Extras package or something?
I think TextPrinter and maybe TexUtil could be provided just for helping the beginners get something running quickly.
#10
It has been moved to OpenTK.Compatibility.
The will continue to work, but unless someone steps up the TextPrinter won't be developed any further. This means that stuff like OpenGL 3.0 or Mono.Pango will not be added.