Jump to content

  • Log In with Google      Sign In   
  • Create Account


Text rendering - Minification?


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
12 replies to this topic

#1 gfxCahd   Members   -  Reputation: 196

Like
0Likes
Like

Posted 14 January 2014 - 02:01 AM

Ok, so I've implemented my own BitMap font system. Using the textures and description file produced by Angel Code's BMFont (http://www.angelcode.com/products/bmfont/) I can display properly formated text (kerning included!) in my project. Since I'm also using a custom spritebatch class, the text rendering is rather fast too.

 

There is just one problem: It looks like utter shite.

You see, I use a font with character size at 50 pixels. Then in my project I use various sizes (all smaller than the original font) which i achieve by scaling. Then I also support various resolutions (yet more scalling for my font). The final result is barely legible (moth-eaten text without mipmaps, smudged-out text with mipmaps).

 

Now before someone suggests SDF (Signed Distance Field) text, I must point out that, although being able to render almost any size of text, at any angle, is a remarkable thing, that my needs consist entirely of displaying scaled-down text, without any rotation. And, I've seen mention that SDFs are not that great with rendering minified text.

 

Another thing I've noticed is the positioning of the individual glyphs on screen. Integer positions are supposed to give a much cleaner result, but how do i manage to do this when I also have kerning involved?

 

So, what is the best way to render minified text?


Edited by gfxCahd, 14 January 2014 - 02:04 AM.


Sponsor:

#2 C0lumbo   Crossbones+   -  Reputation: 2173

Like
1Likes
Like

Posted 14 January 2014 - 02:05 AM

Trilinear filtering might help with the mipmapping a little perhaps.

 

However, probably the best/easiest fix is to generate some more fonts which you can switch to when you're rendering. So instead of a one-size-fits-all 50 pixel source, have a 15 pixel, a 30 pixel and a 50 pixel version. You might even find that your chosen typeface looks poor at 15 pixel, so you might need to use a different font for very small characters, one which has been optimised specifically for small sizes.



#3 gfxCahd   Members   -  Reputation: 196

Like
0Likes
Like

Posted 14 January 2014 - 02:13 AM

Trilinear filtering might help with the mipmapping a little perhaps.

 

However, probably the best/easiest fix is to generate some more fonts which you can switch to when you're rendering. So instead of a one-size-fits-all 50 pixel source, have a 15 pixel, a 30 pixel and a 50 pixel version. You might even find that your chosen typeface looks poor at 15 pixel, so you might need to use a different font for very small characters, one which has been optimised specifically for small sizes.

 

Hm, I wonder, maybe there is a way to use smaller fonts as mipmaps? I mean, I generate one large 64pix font texture, then use a 32pix, 16pix, 8pix etc font generated by BMFont, and put these in the original 64pix texture as its mipmaps.

 

Or do character details (width, xadvance etc...) do not change linearly with scalling?

 

Edit: Gah..., after a bunch of experimenting, I can't get BMFont to give a consistent layout of the glyphs. It keeps rearranging them when I specify different texture sizes. I think I'm doing everything correctly (64pix font in a 512x512 texture with 8 spacing on either side of each character, then a 32pix font in 256x256 texture with 4 spacing, but BMFont keeps changing each characters possition.!.....)


Edited by gfxCahd, 14 January 2014 - 03:41 AM.


#4 laztrezort   Members   -  Reputation: 956

Like
0Likes
Like

Posted 14 January 2014 - 07:57 AM

I implemented BMFont rendering in an OpenTK project, and remember having to tweak the output settings a bit to get the text to look good.  I don't have the config file I ended up using handy, but I remember it taking some experimentation.  The end result is pleasing to me, with both min- and mag- (I just let OpenGL create the mipmaps).  Of course, I suppose it depends on how much scaling you are doing.

 

Maybe you can post some screen shots of your results, maybe someone might see something there?

 

EDIT: ah, found the config I'm using:

# AngelCode Bitmap Font Generator configuration file
fileVersion=1

# font settings
fontName=Open Sans
fontFile=
charSet=0
fontSize=24
aa=1
scaleH=100
useSmoothing=1
isBold=0
isItalic=0
useUnicode=1
disableBoxChars=1
outputInvalidCharGlyph=0
dontIncludeKerningPairs=0
useHinting=1
renderFromOutline=1
useClearType=0

# character alignment
paddingDown=0
paddingUp=0
paddingRight=0
paddingLeft=0
spacingHoriz=1
spacingVert=1
useFixedHeight=0
forceZero=0

# output file
outWidth=256
outHeight=256
outBitDepth=32
fontDescFormat=1
fourChnlPacked=0
textureFormat=png
textureCompression=0
alphaChnl=0
redChnl=4
greenChnl=4
blueChnl=4
invA=0
invR=0
invG=0
invB=0

# outline
outlineThickness=0

# selected chars
chars=32-126

# imported icon images


Edited by laztrezort, 14 January 2014 - 08:02 AM.


#5 gfxCahd   Members   -  Reputation: 196

Like
0Likes
Like

Posted 14 January 2014 - 09:36 AM

Your spacing is too small (1 pixel). If you want mipmaps the spacing should be at least 8. (Personally I think mips 0, 1, 2, are enough, anything smaller is completely useless, so a spacing of 4 should be good enough).



#6 laztrezort   Members   -  Reputation: 956

Like
0Likes
Like

Posted 14 January 2014 - 10:06 AM

I don't have the complete code base in front of me, but I believe these are the settings that worked fine for my rendering implementation (likely I was using texture clamping).  That said, the exact settings depend on the rendering methods used.



#7 L. Spiro   Crossbones+   -  Reputation: 13008

Like
0Likes
Like

Posted 14 January 2014 - 09:27 PM

How are you generating the mipmaps?

If you are letting Direct3D or OpenGL do it for you it is expected to look like crap; they default to a box filter.

 

If you have to do it through image filtering, the best you can use is a Kaiser filter.

But for best results, the tool should be able to redraw all the font images at half resolutions and create a mipmap chain for you.  If this is not possible, use a Kaiser filter for sharper scaled-down text.

 

 

L. Spiro


It is amazing how often people try to be unique, and yet they are always trying to make others be like them. - L. Spiro 2011
I spent most of my life learning the courage it takes to go out and get what I want. Now that I have it, I am not sure exactly what it is that I want. - L. Spiro 2013
I went to my local Subway once to find some guy yelling at the staff. When someone finally came to take my order and asked, “May I help you?”, I replied, “Yeah, I’ll have one asshole to go.”
L. Spiro Engine: http://lspiroengine.com
L. Spiro Engine Forums: http://lspiroengine.com/forums

#8 gfxCahd   Members   -  Reputation: 196

Like
0Likes
Like

Posted 15 January 2014 - 03:19 AM

Hi Spiro.

 

I generate the font by converting the png file I get from BMFont to dds,

using Paint.Net, A8R8G8B8, with supersampling for the mipmaps.

I am pretty sure this method creates the best mipmaps possible, but please correct me if I'm wrong.

 

(Actualy, I first edit the BMFont png file to premultiplied alpha, using Pixelformer,

as I do all my drawing with a blendstate that expects premultiplied textures.)

 

In my shader, I use trilinear filtering, and a mipmap bias of -0.65f.

This does help a bit, but not by much.

 

Here is the result:

example.png

 

I have come to the conclusion that there are two problems. First, the mipmaps are awful:

font.png

 

Mipmap 1 is visibly worse than what BMFont would create at half of the originals pixel width.

Also the subsequent mipmaps are worse than useless.

 

 

Then there is the fact that screen positioning plays a very big role in how the final text will appear. And it is not a simple matter of setting the glyph positions to integer values. For example, the left square bracket '[' looks good when you force its left coordinates to integers, while the right square bracked ']' needs its right coordinate to be an integer. Nevermind how all this would ruin the effect of kerning.


Edited by gfxCahd, 15 January 2014 - 03:25 AM.


#9 Erik Rufelt   Crossbones+   -  Reputation: 3209

Like
1Likes
Like

Posted 15 January 2014 - 03:38 AM

To get proper mipmap usage you need padding between the glyphs that match the number of mipmaps. For example if you have 2 mip-levels (original image + one scaled to half size), the glyphs must be aligned on 2-pixel boundaries and must have two pixels padding between them (to avoid texture filtering to cause bleeding of adjacent glyphs in the texture).

If you need another mip-level, that requires 4-pixel alignment, and the next 8 pixels.

 

If your original glyphs are 32x32 and you allow two extra mip-levels that will be 16x16 and 8x8 and smaller glyphs than that probably aren't readable anyway so I wouldn't allow smaller mipmaps but let them pixelize if they get smaller (so just create the texture with only 3 mip-levels).



#10 gfxCahd   Members   -  Reputation: 196

Like
0Likes
Like

Posted 15 January 2014 - 03:45 AM

^ Yeah. In the font i posted, I use a spacing of 4 pixels (on either side), and in my project, I specify that only the first 2 mipmaps are used (D3DX10_IMAGE_LOAD_INFO MipFilter = 3)


Edited by gfxCahd, 15 January 2014 - 03:47 AM.


#11 L. Spiro   Crossbones+   -  Reputation: 13008

Like
1Likes
Like

Posted 15 January 2014 - 04:01 PM

please correct me if I'm wrong.

Compare your results to mine and decide for yourself.
 
font2.png

There are many factors in making proper mipmaps.
#1: A proper and sharp filter, such as a Kaiser filter. Judging by your first mipmap, Paint.NET uses a pretty crappy down-sampling filter. I judge based only on the first mipmap because you can’t make mistake #2 with it, which is-
#2: Every mipmap level must be calculated from the original full-sized image. It is a mistake to halve the image, then halve that image, then halve that image etc. down to 1×1. Halve the main image. Then fourth it. Then eighth it.
#3: Premultiply alpha before generating the mipmaps. But you are already doing this.


There is no real solution to on-screen positions. Your focus should only be on the mipmap quality. Use my image and check the result.


L. Spiro


Edited by L. Spiro, 15 January 2014 - 04:10 PM.

It is amazing how often people try to be unique, and yet they are always trying to make others be like them. - L. Spiro 2011
I spent most of my life learning the courage it takes to go out and get what I want. Now that I have it, I am not sure exactly what it is that I want. - L. Spiro 2013
I went to my local Subway once to find some guy yelling at the staff. When someone finally came to take my order and asked, “May I help you?”, I replied, “Yeah, I’ll have one asshole to go.”
L. Spiro Engine: http://lspiroengine.com
L. Spiro Engine Forums: http://lspiroengine.com/forums

#12 gfxCahd   Members   -  Reputation: 196

Like
0Likes
Like

Posted 16 January 2014 - 06:29 AM

Wow, that made a difference, thanks!

Using the Nvidia plugin for photoshop, with the Kaiser filter and the format 32.32.32.32f ABGR 128bpp

I got a result almost as good as yours. What software are you using?

 

#2: Every mipmap level must be calculated from the original full-sized image. It is a mistake to halve the image, then halve that image, then halve that image etc. down to 1×1. Halve the main image. Then fourth it. Then eighth it.
 

 

Hm, doesn't the NVidia plugin do that automatically?



#13 L. Spiro   Crossbones+   -  Reputation: 13008

Like
0Likes
Like

Posted 16 January 2014 - 12:31 PM

Photoshop CS2.
Its normal reduction routine is very similar to a Kaiser filter (or is a Kaiser filter) but you have the option of adding a sharpening pass after it. A Kaiser filter alone is usually enough but you can also resharpen each mipmap for slightly better results.
 

Hm, doesn't the NVidia plugin do that automatically?

I would assume so.


L. Spiro


Edited by L. Spiro, 16 January 2014 - 05:43 PM.

It is amazing how often people try to be unique, and yet they are always trying to make others be like them. - L. Spiro 2011
I spent most of my life learning the courage it takes to go out and get what I want. Now that I have it, I am not sure exactly what it is that I want. - L. Spiro 2013
I went to my local Subway once to find some guy yelling at the staff. When someone finally came to take my order and asked, “May I help you?”, I replied, “Yeah, I’ll have one asshole to go.”
L. Spiro Engine: http://lspiroengine.com
L. Spiro Engine Forums: http://lspiroengine.com/forums




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS