Progress on Text Rendering
August 21, 2020
Hi everyone!
As I have mentioned in the previous post, I took a little break, but I'm now back working on VGC :-) More precisely, I've been working on text rendering for the past few weeks: the image above is one of the very first renders of text in the VGC graphics engine!
Why do I need to implement text rendering?
Typically, when developing a desktop application, showing text on screen is quite easy: you can simply use the built-in text functionality of your chosen UI framework (example: Qt, GTK, Win32, Cocoa, Skia, etc.). Basically, you just write something like "drawText('Hello!')", and you're done.
However, as I explained at the beginning of the year, I decided to implement my own graphics engine so that I can fine-tune it and optimize it to the specific use case of VGC. In fact, the requirements for VGC are somewhat more similar to a 3D game than a typical user interface, not only because we need a 3D view, but also because we need extremely low latency, otherwise drawing doesn't feel good (lag between mouse and drawn line).
Implementing a custom graphics engine is not a decision I took lightly, and one of the things that made me hesitate the most is text rendering. I knew it would be one of the most time-consuming thing to implement: text is more complicated than it seems. Anyway, because of this choice, I need to implement some custom code for rendering text, which most applications typically don't have to do.
What is already implemented?
I've made a lot of progress the past few weeks:
-
I have implemented a new Curve class that supports all the types of curves used in fonts.
-
I have integrated the FreeType library into VGC: this allows me to read font files (TTF, OTF, etc.), extract the outline of each glyph ("H" is a glyph), and convert this outline to my new Curve class.
-
I have integrated the HarfBuzz library into VGC: this allows me, based on a given UTF-8 encoded text string and a given font, to know which glyphs should be drawn, and at which position and size.
-
Finally, I have implemented a "stroke" algorithm, which converts the outline of a glyph into tiny triangles, using adaptive sampling and miter joins that provide sharp and clean corners. These tiny triangles look like this:
What should still be implemented?
There are three big remaining algorithms to implement:
-
Text layout algorithm. this means automatically split a long line into two or more lines if it exceeds some maximum text width.
-
Polygon tesselation algorithm. In the image above, we can see that I'm drawing the "outline" (or "contour") of the text. This is nice but it isn't usually what we need: we need to draw the interior of the text instead, not its outline. In VPaint, I was using a library called GLU for this: it was converting the contour of a face into non-intersecting triangles spanning the interior of the face. However, GLU is now deprecated, so I'll need to either use another library, or implement it myself. One good choice of existing library might be libtess2 (https://github.com/memononen/libtess2), which is basically a refactored version of the tesselation functions of GLU.
-
Anti-aliasing algorithm. Without this, the text will look ugly especially at small fonts. The easiest method is to activate multi-sample antialiasing (MSAA), a feature available in good graphics cards which is in fact applied to the the whole render (it cannot be selectively applied to text only). This works well but is generally slow and could introduce lag when drawing, so I'd have to carefully benchmark and decide whether it is a good idea to activate it by default, and how many samples per pixels to use (in the future, this should be a user preference, like in most games). The alternative is to generate more tiny triangles just outside the contour of the glyphs, with a 1px-wide gradient to transparency. This would take more time to implement, but may provide better looks and better performance, especially on computers without a powerful graphics card.
What's nice about the two remaining steps is that they will be super useful in the future! I also need these for rendering VGC faces. I just happen to need it now for text rendering, but even if I hadn't decided to implement some custom text rendering, those are algorithms that were needed anyway :-)
Once again, thanks a lot for your support and patience!
Boris
Stay tuned
Found this news interesting? We can send the next ones straight to your inbox (around twice a month). Or we can simply let you know when VGC 1.0 is released. No spam guaranteed. You can unsubscribe at any time.