Font runtime rendering?

Sorry friend… I’m not on FaceBook… But board games can be fun to code. Thanks for sharing, Hope it works out for you!

I’m curious about the latter. How does Unity and Microsoft Word handle adjusting font size easily?

@Kimimaru Fonts are rendered the same everywhere. The the font is rendered by request with size and decoration parameters. That creates a bitmap containing the glyphs and an atlas of rects describing it. (then on a GL/DX environment the bitmap is loaded as a texture on vram) When you want to print a string the function that does that iterates over the characters and foreach character it finds the belonging rect will be copied to the screen/buffer.

Basically the content pipeline creates the texture and the atlas on compile-time. This disallows you from creating it on runtime. After researching this a bit, this is probably because they are using a third party (c++) library, or API calls (eg. GDI) which are not supported on all platforms.

So either introduce your own font renderer (I personally would go with stb_trutype), or scale the pre-processed spritefonts with the spritebatch.

You can use the alternative I use.

I gave up with spritefonts for anything other than debug a long time ago. Instead I swapped to signed distance field fonts.

There is a freeware tool available (sorry cannot remember the name of it at the moment) which generates the required texture and supporting text file.

It requires a renderer that can render quads at floating point positions, so you cannot use spritebatch, but that is trivial to create.

Then a custom shader. Which is simple.

`float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
float mask = tex2D(tex,input.UV).a;
float4 clr;

clr.rgb = colour.rgb;

if( mask < 0.4 )  
	discard;
else  
	clr.a = 1.0;

// do some anti-aliasing
clr.a *= smoothstep(0.125, 1.00, mask);

return clr;

}

`
The good thing is that the font can be scaled at runtime to pretty much any size you want.

You can also change the shader to do things like shadows, glows, borders, all sorts of things to liven up you text rendering.

If you think it is worthwhile I could package my stuff up and publish it somewhere, but it is really easy to do.

Anyway, might be a way to solve your problem quickly.

Good luck.

Stainless

1 Like

@StainlessTobii
Hi,

Thanks for your response! Your sollution seems pretty decent! But since my last post i’ve already implemented the “break point” sollution in my game engine with support for:

  • Single Line
  • Multi Line
  • Render in Rectangle Area’s
  • Alignments (left, center, right) (using MesureString())

The resolving text is integrated in my engine eco-system and it would be a shame making it this nice and then not use it! But your solution still might help others, perhaps you can still make it available on request for others!

Thanks a lot for sharing!

It requires a renderer that can render quads at floating point positions,

I dunno about the loading but spritebatch itself internally renders all the glyphs as quads.

When you make a call to spritebatch’s drawstring the letter evaluates to a rectangle that is stored in the spritefonts glyphs but that rectangle is divided by the images width and height and is turned into a floating point quad.

You can actually peel out the glyphs redo them and replace them along with the texture in the spritefont.
If you were so inclined to go thru all that.
In the case of getting rid of different sized fonts and just loading the largest ones basically you could then just rescale them down in both the glyph and texture with get set data and create them on the fly i suppose. You would have to keep track of the textures you created to dispose of them though on game end.

Hi willmotil,

You don’t have a method on spritebatch that takes a RectangleF as input, so what happens is the glyphs are slightly out of position and you get little gaps in words that just look awful.

A quad renderer is so much easier to work with, and you can batch it just like spritebatch.

Using signed distance field fonts you only have, want, and need a single texture. That’s a big thing if you want multiple font faces in your game. The less texture space we use for font rendering the better in all games, then we can have 4K textures where we really need them, like skin shaders.

I will see if I can strip my stuff out into a demo and publish it somewhere.

You don’t have a method on spritebatch that takes a RectangleF as input, so what happens is the glyphs are slightly out of position and you get little gaps in words that just look awful.

Are the physical pixels that make up your screen or your image described or stored discrete units ?.. yes they are so they ultimately equate to integer values. The spritefont.xml that the pipeline produces has a field that allows you to turn on kerning which you should do.

A quad renderer is so much easier to work with, and you can batch it just like spritebatch.

That maybe true but have you compared the raw speed you get with your renderer to spritebatch drawstring ?.

Using signed distance field fonts you only have, want, and need a
single texture. That’s a big thing if you want multiple font faces in
your game. The less texture space we use for font rendering the better
in all games, then we can have 4K textures where we really need them,
like skin shaders.

Well im not familar with signed distance fields.
If the goal is to eliminate loading multiple resolution fonts with just one im all for that.

The spritefont class is intended for speed it was never intended to handle a game that wants to use 10 different fonts. That said if you want this in monogame we would first have to know how to incorporate it into spritebatch and if it is possible jjag or nkast did the optimizing on spritebatch i did some initial work on unrolling the matrices but i put up all my working code with simplified comparisons and demo examples. It’s open source meaning anyone can contribute to limits of there ability to do so.

Like i said though internally spritebatch is just using what amounts to floats,

In truth using a rectangleF is a non issue once you realize all textures have pixel width and height and every quad translates to physical pixel coordinates.

/// <summary>
/// Submit a text string of sprites for drawing in the current batch.
/// </summary>
/// <param name="spriteFont">A font.</param>
/// <param name="text">The text which will be drawn.</param>
/// <param name="position">The drawing location on screen.</param>
/// <param name="color">A color mask.</param>
	public unsafe void DrawString (SpriteFont spriteFont, string text, Vector2 position, Color color)
	{
    CheckValid(spriteFont, text);
    
    float sortKey = (_sortMode == SpriteSortMode.Texture) ? spriteFont.Texture.SortingKey : 0;

    var offset = Vector2.Zero;
    var firstGlyphOfLine = true;

    fixed (SpriteFont.Glyph* pGlyphs = spriteFont.Glyphs)
    for (var i = 0; i < text.Length; ++i)
    {
        var c = text[i];

        if (c == '\r')
            continue;

        if (c == '\n')
        {
            offset.X = 0;
            offset.Y += spriteFont.LineSpacing;
            firstGlyphOfLine = true;
            continue;
        }

        var currentGlyphIndex = spriteFont.GetGlyphIndexOrDefault(c);
        var pCurrentGlyph = pGlyphs + currentGlyphIndex;

        // The first character on a line might have a negative left side bearing.
        // In this scenario, SpriteBatch/SpriteFont normally offset the text to the right,
        //  so that text does not hang off the left side of its rectangle.
        if (firstGlyphOfLine)
        {
            offset.X = Math.Max(pCurrentGlyph->LeftSideBearing, 0);
            firstGlyphOfLine = false;
        }
        else
        {
            offset.X += spriteFont.Spacing + pCurrentGlyph->LeftSideBearing;
        }

        var p = offset;                
        p.X += pCurrentGlyph->Cropping.X;
        p.Y += pCurrentGlyph->Cropping.Y;
        p += position;

        var item = _batcher.CreateBatchItem();
        item.Texture = spriteFont.Texture;
        item.SortKey = sortKey;
    
        _texCoordTL.X = pCurrentGlyph->BoundsInTexture.X * spriteFont.Texture.TexelWidth;
        _texCoordTL.Y = pCurrentGlyph->BoundsInTexture.Y * spriteFont.Texture.TexelHeight;
        _texCoordBR.X = (pCurrentGlyph->BoundsInTexture.X + pCurrentGlyph->BoundsInTexture.Width) * spriteFont.Texture.TexelWidth;
        _texCoordBR.Y = (pCurrentGlyph->BoundsInTexture.Y + pCurrentGlyph->BoundsInTexture.Height) * spriteFont.Texture.TexelHeight;

        item.Set(p.X,
                 p.Y,
                 pCurrentGlyph->BoundsInTexture.Width,
                 pCurrentGlyph->BoundsInTexture.Height,
                 color,
                 _texCoordTL,
                 _texCoordBR,
                 0);
        
        offset.X += pCurrentGlyph->Width + pCurrentGlyph->RightSideBearing;
    }

		// We need to flush if we're using Immediate sort mode.
		FlushIfNeeded();

}

No mate, you have got it all wrong.

Say I am displaying a string “Hello”

In the font definition the width of the H character is 16.25 pixels. I draw the H glyph in the correct place, then come to draw the e glyph.

Using spritebatch I would have to display it 16 pixels away from the start position, and the space between the chars is reduced. This can even result in glyph’s overlapping.

Using a glyph batch, which is just as fast as spritebatch, the glyph is in the correct position, just one of the pixels gets written to twice to handle the overlap when we get to physical pixels.

However the very nature of signed distance fields means this is not visible.

When I first wrote a SDF font renderer, I thought like you, but when I actually tested it, the result was garbage. It took me a couple of hours to figure out that problem. I also screwed up by having point sampling enabled, but that’s another story. :smile:

I have finished a quick demo which shows 4 different shaders (normal, inverse, shadow, and outline) working with the same two SDF fonts.

Displaying at 4 different font sizes.

Just got to find somewhere to put it.

Okay decided just to stick it on my website.

example project

I use google drive its good for small projects.

Anyways i still don’t see the big advantage. Or any problem with glyph’s overlapping, using spritebatch.
My complaint with spritebatch isnt quality its the lack of automatic mipmaping when scaling down.
But that is being pretty nit picky.

Its like a 45 point sized font (but the bigger the better with spritefont), that is resolution scaled via a design time w h proportion + user scaled. With user resizing on in a shrunk window.

Looking at that code i doubt its anywere near as fast as spritebatch. You would have to do some testing to make that claim you might be suprised what you find.

You can send in effects to spritebatch begin.
As well as a matrix, meaning you can use it for 3d text. ect…

Also as far as garbage all numerical text will generate it.

            spriteBatch.Begin();
            GraphicsDevice.SamplerStates[0] = new SamplerState() { Filter = TextureFilter.Point, AddressU = TextureAddressMode.Clamp, AddressV = TextureAddressMode.Clamp };

            Vector2 txPos;
            Vector2 txSize;

            txPos = VirtualPosToScreen(.025f, .33f);
            txSize = ScreenReScale(.5f ,1f);
            spriteBatch.DrawString(regfont, "Verdana mg scale .5, 1f", txPos, Color.Moccasin, 0f, Vector2.Zero, txSize, SpriteEffects.None, 0f);

            txPos = VirtualPosToScreen(.025f, .45f);
            txSize = ScreenReScale(.5f ,.75f);
            spriteBatch.DrawString(regfont, "Verdana mg scale  .5, .75", txPos, Color.Moccasin, 0f, Vector2.Zero, txSize, SpriteEffects.None, 0f);

            txPos = VirtualPosToScreen(.025f, .55f);
            txSize = ScreenReScale(.5f ,.5f);
            spriteBatch.DrawString(regfont, "Verdana mg scale .5, .5", txPos, Color.Moccasin, 0f, Vector2.Zero, txSize, SpriteEffects.None, 0f);


            // draw the font.
            spriteBatch.Draw(regfont.Texture, new Vector2(400, 400), Color.Blue);

            spriteBatch.End();

Dunno how you can draw bottom up ah that seems so un-natural to me.

        Vector2 VirtualPosToScreen(float x, float y)
        {
            var v = new Vector2(x, y);
             return v * new Vector2(GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height);
        }
        Vector2 ScreenReScale(float x, float y)
        {
            var v = new Vector2(x, y);
            Vector2 screenScaling = new Vector2(GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height) / new Vector2(800f, 600f);
            return v * screenScaling;
        }
        Vector2 ScreenReScale(float s)
        {
            Vector2 screenScaling = new Vector2(GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height) / new Vector2(800f, 600f);
            return s * screenScaling;
        }

Sorry im not trying to put it down or boast about the current spritebatch its got drawbacks, im just saying this.

That is nice but its not as simple or functional and i doubt its as fast as the current implementation and adding it in would be a lot of work to bring it on par with spritebatch, more then what is shown in that project far far more.

The image you have posted shows the massive problem that stops me using this technique.

Scaling artifacts. They are everywhere. Which for me makes it un-usable for font rendering.

The beauty of Signed Distance Field Fonts is you don’t get scaling artifacts. You get crisp clean edges. You do get a certain amount of fading out of text when you down sample a lot, but it’s still crisp and clean.

We can argue about what is faster, a quad batch or spritebatch all day. Maybe that is a good argument we should have somewhere. We may come up with some ideas for speeding up spritebatch, or getting rid of scaling artifacts, or something. I don’t think this thread is the right place for that though. ( I also think it’s going to come down to wavefronts and other GPU specifics which don’t fit the Monogame ethos… but that’s another topic). :smiley:

Anyway, anyone interested in font rendering now has a thread to read :grin:

OMG, I have created a monster :imp: (:P)

A wild solution has appeared xD

I played with your code, and got it to generate less garbage just by moving

RasterizerState stat = new RasterizerState(); SamplerState ss = new SamplerState();

to fields.

The GC was called enough that CPU time was a bit higher. Not ideal.

After my change, less garbage is spewn out.

Hope that your game may benefit.

There are historical reasons why Microsoft used the Content Pipeline for Fonts:

  • Licensing. TrueType fonts have permissions associated with them indicating how they can be used without paying royalties. The one required to distribute a ttf file, Installable, was not granted on most fonts shipped with Windows.
  • Cross-Platform. By integrating rasterized fonts as textures, the fonts will have a consistent look no matter what fonts are installed locally.
    I addition to the points above MonoGame, must be able to maintain consistency across many different platforms, each with it’s own font processing/rendering capabilities.

Please note that anything above ~72 points (not px) is simply a scaled version of the 72 font. TTFs include hints to apply when rendered small 6-8, medium 10-15, and large fonts. The exact sizes and number of divisions vary with each font, but were defined with printing in mind, therefore, larger font sizes return a scaled version of the largest hint available.
My recommendations for divisions would be 8,9,10,12,16,18,24,36,72 and scaling when needed. That should be around 1 MB per font.
My solution to this problem was to break up the distributions by screen size. For example, on Android, I created builds of the app targeting small screens with small fonts(8,12) … Through 4k TV(74,96). Similar on IOS. Desktop users typically don’t care about file size.

All fonts are not rendered the same everywhere. They are similar to the process you describe, but each vendor has it’s own implementation, API and font formats(See Color fonts). For example, the emojis you utilized in previous posts were converted to PNGs by the web server and not rendered as a font.

My point is run-time font rendering has its place(CJK and emoji, Where sizes balloon by combinations.) In my opinion, it is not needed for general use within the framework, as there are other viable solutions available.

Thanks, I did not know that, I always just thought that it’s made out of vectors which can be rasterized to any size. Also it seems very outdated in a mush higher pixel density era we are living in, which presumably will keep evolving in the future.

Implementations only differ in features. The core process is the same, so generalizing a bit does not seem out of place.

They are converted by the browser and I don’t see what a feature like that has to do with “font rendering”. It just replace some text by images and works on a different level (post font rendering) then the fonts.

:0) --> <img ... />

Is not font rendering but search and replace.