How do i increase the size of a bitmap font without having it get pixelated, in libgdx?
-
\$\begingroup\$ You can't; how could you invent pixels at a correct location? \$\endgroup\$– Vaillancourt ♦Commented Apr 23, 2016 at 18:51
-
\$\begingroup\$ The bitmap font doesn't have information you want, but there are some clever algorithms that try to guess, based on heuristics. For example this page has an example image with low resolution letters; click "Our SR Result" to see what their algorithm can produce at a larger size. There are many algorithms but they're designed for pixel art not for bitmap fonts. The easiest thing would be to use a non-bitmap font… \$\endgroup\$– amitpCommented Apr 23, 2016 at 19:32
-
\$\begingroup\$ Or include multiple pixel sizes of the font you use is also common in this case. \$\endgroup\$– Patrick HughesCommented Apr 23, 2016 at 20:25
4 Answers
There are some pretty good suggestions in the other answers here. I would like to add one that is more libgdx specific (so you hopefully don't have to reinvent the wheel).
BitmapFonts in libgdx are raster(bitmap) images and will pixelate just like any other raster image when scaled. The solution that I usually use is that if I want a font in different sizes, then I have to pre-create a font for each size. A great tool that does this that is packaged with libgdx is Hiero. Managing all these fonts can be easy if you use libgdx's AssetManager
The second option that libgdx uses is TTF fonts. TTF files are vector images so they scale perfectly. Unfortunately, the downside is that libgdx cheats a little bit because rather than keeping the TTF file in memory and drawing from it, you use the TTF file to create a bunch of BitmapFonts
that are pre-scaled without pixelation and then you actually draw the BitmapFont
. You have to add the FreeType extension to your project to do this. Once you do that, you can programatically create scaled fonts to your heart's content like so
FreeTypeFontGenerator generator = new FreeTypeFontGenerator(Gdx.files.internal("fonts/myfont.ttf"));
FreeTypeFontParameter parameter = new FreeTypeFontParameter();
parameter.size = 12;
BitmapFont font12 = generator.generateFont(parameter); // font size 12 pixels
parameter.size = 32;
BitmapFont font32 = generator.generateFont(parameter); // font size 32
generator.dispose(); // don't forget to dispose to avoid memory leaks!
//Then use your fonts
Using TTF fonts is more processing, but the upside over BitmapFonts is that you can change the font sizes in the code all you want.
-
\$\begingroup\$ Using FreeTypeFontGenerator is the better approach to scale font. There is a tutorial on LibGDX.info here libgdx.info/basic-label \$\endgroup\$– JulienCommented Jan 12, 2017 at 22:34
You will have to use signed distance field font rendering.
The generated font's pixels represents the distance from the edge rather than the opacity.
Where 0.5 (0x7F) is the edge of the font, 0.0 (0x00) is the outside, 1.0 (0xFF) is the inside.
How wide this extends is up to you, in the grey scale example image I'm using a 2-pixel distance so it reaches 0x00 2 pixels away from the edge and 0xFF 2 pixels inside the edge. It is possible to use a half-pixel border (ie: a regular anti-aliased font bitmap) to avoid writing your own font converter/generator but depending on the AA technique it may create wobbly edges.
To draw using a pixel shader you render > 0.5 as opaque, < 0.5 as transparent. On fixed-pipeline hardware the Alpha Test function does the trick
On the left is the distance field texture. On the right is the result. When interpolated the approximated curve is recreated by the nature of the interpolation (bottom part):
Below is a screenshot of a 24 pixel high distance field font scaled up to 256 pixel high rendered on 3D hardware. You can see some slight distortion of the corners but no pixelation.
Also, make sure to not compress the texture. It needs to be stored as a 8bits per channel or at worst 4bits per channel. A texture compressed using ETC, DXT or other compression will loose too much precision to recreate the edges properly.
A common trick is to use all 4 channels (red, green, blue, alpha) to encode different characters as 4 grey-scale textures inside a single texture. This is what it looks like:
I stumbled upon this answer after getting frustated for 2 days and get this really simple solution. Though I wonder why is there no proper documentation for this most basic task.
If you want to scale bitmap font using hiero, you can use:
BitmapFont font = new BitmapFont(Gdx.files.internal("data/myfont.fnt"));
font.getData().setScale(0.15f);
Or, you can even use gdx-freetype if you want to scale font during runtime as:
public BitmapFont generatefont() {
FreeTypeFontGenerator generator = new FreeTypeFontGenerator(Gdx.files.internal("data/myfont.otf"));
FreeTypeFontGenerator.FreeTypeFontParameter parameter = new FreeTypeFontGenerator.FreeTypeFontParameter();
parameter.size = 32;
parameter.magFilter = Texture.TextureFilter.Linear;
parameter.minFilter = Texture.TextureFilter.Linear;
BitmapFont font32 = generator.generateFont(parameter); // font size 32 pixels
font32.getData().setScale(0.15f);
generator.dispose();
return font32;
}
Both gives pretty fair result.
-
1\$\begingroup\$ Note that sometimes font licences treat bundling the source ttf/otf file into the software differently than including a rasterized version. So generating your bitmaps on demand at runtime might require a more expensive licensing tier. \$\endgroup\$– DMGregory ♦Commented Apr 21, 2020 at 15:26
You could avoid the blur effect when upscaling by using a high-resolution image font and drawing it downscaled. Example: if your font is supposed to be drawn with a 24px size, use a sample at 48px and draw the text with a scale ratio of 0.5, because downscaling won't make aliasing noticeable.
This does work fine as long as your font image strip doesn't take too much space to be saved. Also, having different scale sample of the same font can be handy not to cause too much overhead on the rendering - downscaling a 60px font to a factor of 0.25 is slower than having the same font in 15px, the latter can be just drawn at its original size.