Smooth Font Scaling Prototype

Hey all,

I’ve dealt with font scaling all my damn career, it’s just one of those annoying things that seems to keep coming up no matter where I work. I’ve seen it implemented a bunch of different ways in that time but it’s always ended up kind of quirkly/awkward, with annoying jumps in the visuals as you move between the font breakpoints.

Anyway, I had this idea I posted a while back (Font runtime rendering?) to select the closest font size, then scale the texture in between those breakpoints so it looks smooth, without having to go down any complicated paths like Signed Distance Field fonts. It’s been a while, but I finally got around to prototyping it :stuck_out_tongue: Mostly because it came up at work and there was some question as to whether or not the scaling would look like garbage.

If you run this, you can left-click and drag to move the text around, and left-click and drag the handle in the bottom right to resize. I only bothered to implement scaling to width, but it’s easy enough to do the same for height.

The end result actually looks fairly good. When scaling between font sizes at small sizes, there’s a few jumps, but I think this could actually be mitigated by having less font breakponts in the lower end. Right now I cache every font size for Calibri that paint .net told me existed, but I think if I went straight from 8 to 20, it might go a little more cleanly. For any single static font though, you don’t really notice. Also, the scaling for really small fonts (ie, when you’re scaling smaller than a font size of 8, which is the minimum for Calibri apparently), it starts to get hard to read. This is probably obvious because there’s just not enough pixels to create readable text past a certain point.

If you’re looking at this project, the files that will probably be of most interest to you are…

  • View\TextEntityView.cs
  • Model\SimpleFontScaler.cs
  • Model\FontBank.cs

My intent here was to get a proof of concept going, so the code isn’t super optimized. About the only concession to optimization I made was to cache the texture that gets generated after a size change, so you only draw that cached texture.

Some optimizations that one could look into are…

  • FontBank exposes two methods, GetNextSizeLarger and GetNextSizeSmaller. This takes in a FontDescription and will search the FontBank for the next available size (in whatever direction) that is stored in the FontBank for that font. Right now this is just a linear search that starts at the beginning of the list and iterates until it finds a matching size, then takes the next size in either direction. There are much more intelligent ways to go about this :wink:
  • SimpleFontScaler uses a SpriteFont.MeasureString as the basis of its comparison. I used to know the underlying mechanism by which this worked but have since forgotten. Either way, I suspect it gets the size of each glyph in the SpriteFont for each character in the string and builds it that way. Since these glyphs are pre-rendered by the Content Pipeline, this isn’t too bad, but it could still be considered slow by some. Another option to consider might be to simply average the size of every glyph in the SpriteFont, cache it somewhere, and just multiply it the number of characters in the string to get a ballpark string size. Since we’re scaling the end result anyway, and since that scaled result looks fine, we probably don’t need to be super precise on this measurement. Just do a quick calculation to get close and let bitmap scaling do the rest.
  • Not so much an optimization, but more of a convenience issue… I had to create a SpriteFont for each breakpoint I wanted to use. This was a pain in the butt… just for Calibri. If you need a lot of fonts, you probably want to create some kind of tool to generate these for you, or look into a method of generating sprite fonts at runtime. There are also other font rendering solutions that could be used.

I’m sure there are many more places that things could be cleaned up and the performance increases, but those are the key ones that stuck at as I was implementing. I just didn’t care enough to overcome them because my intent was to see how the scaling looked :slight_smile:

Anyway, that’s enough rambling from me. The project is there should anybody find this kind of thing useful.

2 Likes

Cool stuff!

Question:
isnt it possible to read the font size from the spritefont file as it contains for exempel <Size>10</Size>. That way you dont have to sufix the file name with the size. Or are you doing it this way for some reason i dont understand :slight_smile:

Unfortunately, it’s not a property exposed by SpriteFont. Also, at runtime, those files are .xnb, so I couldn’t even process the XML directly if I wanted to. I’m definitely sure that there are other, probably better, ways to do this. I just went for simplicity and put the size in the name, haha.

All that’s really important is that you have a way to get a font by its size. How you get there is ultimately up to you :slight_smile:

I was thinking some build time step but i hear you not the point of the code.

I basically solved my Fonts UI scaling temporally by rounding out the numbers and just using draw with scale. It looks like shit but you can read it. :laughing: I was going to make it better later but i like you solution. GJ!

1 Like