4

Note: This is a direct copy of a question I asked on Stack Overflow which was put on hold for being off-topic and suggested to be asked here instead.

I'm writing a game in C#, monogame. It's cross-platform and tested on Windows and Linux. The game is mostly text-based. It takes place in an operating system environment so as you'd expect it's 2D, not very fancy lighting or anything, and a lot of text.

Text in this game has 3 requirements... 3 major requirements.

  • It must not severely affect the framerate.
  • We should be able to say "I want this string rendered in this font family at this size in points, with(out) bold, with(out) italic, with(out) underline and with(out) strikeout, and I want the text to be aligned to the left/center/right, and have word or character wrapping applied if the string's width exceeds this amount of pixels.
  • It must be readable to the player, but also anti-aliased.

So, MonoGame's SpriteFont system does NOT work for this as it only satisfies the first requirement, speed.

We played around with the following libraries/technologies on both platforms:

  • GDI+ (System.Drawing, like .NET 1.x Windows Forms)
  • GDI (System.Windows.Forms.TextRenderer, WinForms text rendering post .NET 2)
  • Custom C++ wrapper for pangomm/cairomm, utilized through P/Invokes

And here's a comparison of the three libraries on the two platforms and how they (un)satisfy the above requirements.

GDI+

  • Speed: Very slow as all rendering is done on the CPU, no GPU acceleration. In some cases, when text is constantly being re-rendered (like when you constantly hold the Enter key in something like a Bash prompt and it keeps on printing user@host:~$ or similar to the console) the frame-rate can go as low as 2FPS. With a caching system in place you can get unstable 60FPS as you're not actually rendering the same string for more than a single frame until the cache is cleared but this does not help in the case where there are constantly different strings to render that haven't been cached.
  • Usability: We have System.Drawing.Font which can satisfy the ability to specify what font face, size and style to render in, and we can wrap text by specifying a max pixel width, however as most people may tell you (and I have seen in experience) text measurement can be very unreliable.
  • Readability: Due to the fact text measurement can be unreliable, you may allocate a bitmap the same width and height of the measured text, but when drawing, you may notice that a little bit of the end of the string can end up cut off due to the rendered width/height being greater than the bitmap texture you've allocated. Also, if you anti-alias text to get rid of pixelation/jagged edges, some fonts will not be affected at all (namely Tahoma and Lucida Console) and will still draw in their aliased form, and drawing with ClearType enabled makes the text feel extremely artifacted. In some cases, even antialiasing text on certain fonts can make the text hard to read for certain players. Since readability is hard to get right in GDI+ due to all these discrepancies, it does not pass my readability requirement even remotely closely.

To make matters worse, I'm going blind, so if players who are NOT going blind and see perfectly are reporting readability issues in the game, imagine how difficult it is for me, the blind guy, to actually develop it.

GDI (WinForms TextRenderer)

  • Speed: It is slightly faster than GDI+ in my testing, however, keyword, slightly. I can still get the framerate down very low doing the same thing, but it does tend to pop back up quicker than GDI+. It's still not great though as doing the "hold Enter in a terminal for a long time" test on an actual terminal would NOT cause a slowdown like that.
  • Usability: Since it uses the System.Drawing.Font struct like in GDI+ it's perfectly easy to specify the font family, size, and style, however I have not yet worked out how to properly wrap text, like, I can do it, but it's dodgy at best. Text that DOES get wrapped seems to be aligned to the center of the layout rectangle, and I can't seem to find a way to change that. It's actually less usable than GDI+ in that regard, but the MeasureText method can be called statically and without a device context, which is much better than GDI+.
  • Readability: No readability problems for me, text is perfectly anti-aliased no matter what font you use (seems like the antialiasing is done by the renderer and not done relying on the font author to provide antialiased glyphs) so it's a perfect pass on that regard however I notice that there seems to be a black artifact on certain glyphs, but it may just be my eyes messing with me.

So... better than GDI+ in readability, worse in usability, and only slightly better in speed. Would fit better where it belongs - in WinForms - but not in a game.

C++ pangomm/cairomm wrapper

I haven't been able to perform proper testing of the C++ wrapper library as I was not the one who coded it, it was developed internally by another programmer on the team who happens to use Linux and not Windows (in fact he can't stand Windows), but according to his testing on Linux...

  • Speed: Definite pass. He gets 60FPS constantly even when doing the "press Enter a bunch of times really fast" test from before, and since the actual rendering is being done in an unmanaged environment, you can expect a massive speed boost as the CLR isn't calling all the shots.
  • Usability: It is specifically coded to support everything required by the game, and supports everything I need it to do. Pass.
  • Readability: I haven't seen text rendered by the code myself, I'll get into that in a bit but judging by the fact he's not worked directly on the c++ code in a while and likes the way it looks, and judging by my past experiences with GTK# (which uses Pango for rendering), I can say it's a pass.

One problem though. I don't know C++, I don't know how to compile C++ on Windows, minus setting a Visual C++ project to run make when I build the code in VS, and I can't find any pre-compiled binaries for pangomm OR cairomm for Windows. I'd have to compile them myself, which I don't want to have to do, as I'm a C# programmer, not a C++ programmer. I'm experienced with msbuild, not make, and I'm not going to take the time to learn how makefiles are constructed and how to compile things for each platform I want to support. It's too much work for a couple dozen lines of internally-developed code.

Long story short, this is a perfect approach for Linux, in fact, it is the perfect approach for Linux and I plan to get compiled binaries of the library in an Ubuntu PPA for the game (as I do know how to compile it on Ubuntu thanks to the guy who wrote the code telling me what packages it depends on and thanks to Ubuntu for actually having said packages in the main repos, precompiled), but I can't compile it on Windows, making it not really cross-platform.

We need a fourth solution for Windows.

We need something like that third approach, but preferably, pre-compiled, available on NuGet, usable on Windows and usable in C#.

Edit

Seems I didn't really do my homework properly and forgot that StackOverflow doesn't like when you ask for libraries.

What I really want honestly is a better approach to the C++ solution that'll work on Windows, because honestly it works perfectly on Linux, but I'm primarily a Windows developer thus I want it to work well on both and still be easy to set up.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.