Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Monogame rendering text using Graphics.DrawString (instead of SpriteBatch.DrawString)

Is there any downside to using Graphics.DrawString to render a (rather static) bunch of text to an offscreen bitmap, convert it to a Texture2D once, and then simply call SpriteBatch.Draw, instead of using the content pipeline and rendering text using SpriteFont? This is basically a page of text, drawn to a "sheet of paper", but user can also choose to resize fonts, so this means I would have to include spritefonts of different sizes.

Since this is a Windows only app (not planning to port it), I will have access to all fonts like in a plain old WinForms app, and I believe rendering quality will be much better when using Graphics.DrawString (or even TextRenderer) than using sprite fonts.

Also, it seems performance might be better since SpriteBatch.DrawString needs to "render" the entire text in each iteration (i.e. send vertices for each letter separately), while by using a bitmap I only do it once, so it should be slightly less work at the CPU side.

  1. Are there any problems or downsides I am not seeing here?
  2. Is it possible to get alpha blended text using spritefonts? I've seen Nuclex framework mentioned around, but it's not been ported to Monogame AFAIK.

[Update]

From the technical side, it seems to be working perfectly, much better text rendering than through sprite fonts. If they are rendered horizontally, I even get ClearType. One problem that might exist is that spritesheets for fonts are (might be?) more efficient in terms of texture memory than creating a separate texture for a page of text.

like image 988
Lou Avatar asked Jan 25 '15 13:01

Lou


1 Answers

No

There doesn't seem to be any downside
In fact you seem to be following a standard approach to text rendering.

Rendering text 'properly' is a comparatively slow processed compared to rendering a textured quad, even though SpriteFonts cutout all the splining glyphs, if you are rendering a page of text then you can still be racking up a large number of triangles.

Whenever I've been looking at different text rendering solutions for GL/XNA, people tend to recommend your approach. Draw your wall of text once to a reusable texture, then render that texture.

You may also want to consider RenderTarget2D as possible solution that is portable.
As an example:

// Render the new text on the texture
LoadPageText(int pageNum) {
    string[] text = this.book[pageNum];
    GraphicsDevice.SetRenderTarget(pageTarget);
    // TODO: draw page background

    this.spriteBatchCache.Begin();
    for (int i = 0; i < text.Length; i++) {
         this.spriteBatchCache.DrawText(this.font, 
             new Vector2(10, 10 + this.fontHeight * i), 
             text[i], 
             this.color);
    }
    this.spriteBatchCache.End();
    GraphicsDevice.SetRenderTarget(null);
}

Then in the scene render, you can spriteBatch.Draw(..., pageTarget, ...) to render the text. This way you only need 1 texture for all your pages, just remember to also redraw if your font changes.

Other things to consider is your SpriteBatches sort mode, sometimes that may impact performance when rendering many triangles.

On point 2, as I mentioned above the SpriteFonts are pre-rendered textures, this means that the transparency is baked onto their spritesheet. As such it seems the default library uses no transparency/anti-aliasing.

If you rendered them twice as large and White on Black and used the SourceColor as an alpha channel then rendered them scaled back down blending with Color.Black you could possible get it back.

like image 178
Skibisky Avatar answered Nov 19 '22 13:11

Skibisky