[SOLVED] Inconsistency between SpriteBatch DrawString and Graphics.DrawString

Thanks to @Watercolor_Games, a solution has been found. While Graphics.DrawString is not consistent with SpriteBatch.DrawString, TextRenderer.DrawText is and should be used instead. Additionally, TextRenderer.MeasureText also provides a more reliable measurement than Graphics.MeasureString.

What follows is the original post…


Hey,

So I’ve noticed that there’s an inconsistency between SpriteBatch.DrawString font rendering and WinForms Graphics.DrawString rendering, specifically in the spacing between the text. Here’s an image showing the issue…

The left side is rendered with MonoGame, the right side is rendered with WinForms. Notice that the text is wider in the WinForms version than it is in the MonoGame version. The WinForms version also has slightly larger line spacing. Each side is 650x600 pixels.

The WinForms version is rendered using a font loaded from the values found in the spritefont file…

public static Win32SpriteFont FromFile(string contentPath, string assetName, Graphics graphics)
{
    string file = Path.Combine(contentPath, assetName);

    XmlDocument xmlDoc = new XmlDocument();
    xmlDoc.LoadXml(File.ReadAllText(file));
    var fontNameNode = xmlDoc.SelectSingleNode("//XnaContent/Asset/FontName");
    var sizeNode = xmlDoc.SelectSingleNode("//XnaContent/Asset/Size");

    if (fontNameNode == null || sizeNode == null)
        throw new ArgumentException("The specified file does not contain a FontName and/or Size node at the expected XML path.", "file");

    Font internalFont = new Font(fontNameNode.InnerXml, float.Parse(sizeNode.InnerXml));

    return new Win32SpriteFont(internalFont, graphics) { SpriteFontAsset = Path.GetFileNameWithoutExtension(assetName) };
}

I’m wondering if anybody knows a way to get these two rendering closer to each other. I don’t need it to be super exact, just closer. I would actually prefer to have the WinForms version render the same as the MonoGame version.

The context for what I’m doing is that I’m using the WinForms editor to block out UI with text and image elements. I’ve created simple controls that mimic my MonoGame ones so that I can just drag/arrange them around the editor, then spit out some auto-generated code that I can import into my game to create all the things I need.

I’ve prototyped the concept and it’s working quite well; however, the MonoGame version isn’t rendering the same as my WinForms version, which causes me to set the text incorrectly. I know that I can have MonoGame render inside a Form, but this approach was intended to save time and to utilize the existing functionality of the WinForms editor :wink:

Anyway, any help would be greatly appreciated!

The first thing you need to realize here is that WinForms hasn’t used System.Drawing.Graphics.DrawString() for text rendering since .NET Framework 2. Nowadays, it has its own text renderer in System.Windows.Forms.TextRenderer. However the code for using the older renderer is still in WinForms for backwards compatibility.

This is the point of the Application.UseCompatibleTextRendering(false); call in any WinForms project’s Program.cs - it tells the Windows controls NOT to use the old, backwards-compatible text renderer, and instead use the faster and better-looking System.Windows.Forms.TextRenderer. Try making your custom controls use that and see if it gets closer to MonoGame.

The second thing you need to realize is that Windows Forms does not use bitmap/sprite fonts. It instead uses vector-based (TrueType, etc) fonts and rasterizes them at run-time. MonoGame, however, rasterizes them at compile time into what you know as a SpriteFont - because game UIs update a lot more frequently and it’s more important for them to have a smoother framerate. You can’t have that if you’re rasterizing fonts at render time (like WinForms does).

Windows Forms is not designed for games, it’s designed for apps - like MS Office. Stuff that needs to be responsive but doesn’t update NEARLY as often as a game UI would. So it can afford to do software rendering, runtime font rasterization, etc. It’s also aware that these operations are still slow, hence why it makes you Invalidate() or Refresh() a UI element before you can paint on it. Windows Forms doesn’t render the entire UI every frame - only things that need to be refreshed.

The only true way to emulate MonoGame’s text rendering is to literally implement it into Windows Forms. Rasterize your fonts using Content Pipeline, then write your own version of SpriteBatch.DrawString() that can render the font using System.Drawing.Graphics, and code all your UI elements to use that renderer. Of course, this is VERY time consuming if you’re just prototyping a UI for your game.

The best thing to do is to just accept that Windows Forms does things differently, and realize that you’re just prototyping the UI in WinForms and not actually using WinForms for your game. Accept the fact that your text is always going to look off in WinForms - just try to get things in the right spot as far as layout and worry about ugliness of the rendering when it’s time to put stuff into MonoGame. :slight_smile:

1 Like

This is exactly what I needed, thanks! I just did an initial test and it looks much closer… perhaps even exact, but I’ll have to do some more pixel precise checks tomorrow. Even then, I don’t need it to be 100% perfect, just a reasonably good approximation. Using DrawString just wasn’t close enough.

For all the other stuff you’ve said… yep! I knew that going in and fully agree! The whole intent behind this was to just get a quick and dirty means of laying out my objects, utilizing the editor built into Visual Studio. I’ve had some experience making XNA applications integrate into WinForms in the past, and have also designed layout editors from scratch as well, I just didn’t want to get caught up in something that took a lot of time. I’m pretty close to release and am just building the “How to Play” section of my game, but I didn’t want to hand-code all of the locations :wink:

Anyway, thanks a lot for your reply, this will work great!

Glad to hear I helped out :slight_smile:

One thing I’m trying to do for my game a few months down the line is implement Serenity Editor. This editor will have everything - UI editor, theming utility, MGCB frontend, audio cue editor, coding environment (Lua, Python and C#/VB via Roslyn), world editor, cutscene editor, texture editor, etc. It’ll definitely take a LONG time to get working, but, it’ll be worth it. Because it’ll make prototyping a game a LOT easier.

Yea, a more fully featured editor is definitely useful, especially if it uses the same rendering path as the entities that will render in your game haha. I hope your implementation goes well! I’ll check back from time to time :slight_smile:

I don’t really intend for my own engine to be fully featured, just provide the things to make the game that I want to make, then extend from there. So I’ve been keeping an eye around the community for things that I might want to implement on future titles. For right now though, I’m happily building my own stuff and making simple things to get my feet wet.

I’ve always dabbled, but I’ve never made a fully committed effort to release a game until recently.

Just wanted to post back here. Using TextRenderer.DrawText and TextRenderer.MeasureString is bang on what MonoGame does.

There appears to be a very slight inconsistency with the height of a blank line, but that could also be something in my own code and frankly, I’m not too worried about it.

I’ll re-iterate the solution in the first post for anybody who happens across this in the future. Thanks again for your help :slight_smile:

Hey! I wanted to post that, thanks to the help I found here, I was able to accomplish what I was going for and am really happy with how it turned out. If anybody is interested, you can watch a demo video here:

I know there’s a lot of people making editor environments for MonoGame, and I think that’s really great! It’s just nice that, with a relatively small amount of effort, once can utilize the WinForms editor to do a lot of the same things, leveraging functionality that’s already there.

I want to stress that I’m not saying this is a better approach, or bashing those editor projects in any way… not at all! I’m just saying that there are options to get something in place that will quickly allow you to lay out layers with basic elements :slight_smile:

Anyway… thanks again!