Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make font hinting user-configurable. #78830

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
13 changes: 6 additions & 7 deletions data/fontdata.json
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
{
"//1": "If more than one font is specified for a typeface the list is treated as a fallback order.",
"//2": "unifont will always be used as a 'last resort' fallback even if not listed here.",
"//1": "See docs/FONT_OPTIONS.md for how to configure your fonts.",
"typeface": [
"data/font/Terminus.ttf",
{ "path": "data/font/Terminus.ttf", "hinting": "Bitmap" },
"data/font/unifont.ttf"
],
"gui_typeface": [
"data/font/Roboto-Medium.ttf",
"data/font/Terminus.ttf",
{ "path": "data/font/Roboto-Medium.ttf", "hinting": "Light" },
{ "path": "data/font/Terminus.ttf", "hinting": "Bitmap" },
"data/font/unifont.ttf"
],
"map_typeface": [
"data/font/Terminus.ttf",
{ "path": "data/font/Terminus.ttf", "hinting": "Bitmap" },
"data/font/unifont.ttf"
],
"overmap_typeface": [
"data/font/Terminus.ttf",
{ "path": "data/font/Terminus.ttf", "hinting": "Bitmap" },
"data/font/unifont.ttf"
]
}
105 changes: 105 additions & 0 deletions doc/FONT_OPTIONS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Configuring Fonts

Fonts can be configured through changing the config/fonts.json file. This file is created with
default options on game load, so if it doesn't exist try loading the game.

The default options (available in data/fontdata.json) might look like the following:
```json
{
"typeface": [
{ "path": "data/font/Terminus.ttf", "hinting": "Bitmap" },
"data/font/unifont.ttf"
],
"gui_typeface": [
{ "path": "data/font/Roboto-Medium.ttf", "hinting": "Light" },
{ "path": "data/font/Terminus.ttf", "hinting": "Bitmap" },
"data/font/unifont.ttf"
],
"map_typeface": [
{ "path": "data/font/Terminus.ttf", "hinting": "Bitmap" },
"data/font/unifont.ttf"
],
"overmap_typeface": [
{ "path": "data/font/Terminus.ttf", "hinting": "Bitmap" },
"data/font/unifont.ttf"
]
}
```

There are four different font categories: `typeface`, `gui_typeface`, `map_typeface`, and `overmap_typeface`,
which are used for the old-style game interface, ImGui menus, the game display and the overmap, respectively.
If more than one font is specified for a typeface the list is treated as a fallback order. Unifont will always
be used as a 'last resort' fallback even if not listed here.

Fonts can be provided as a list, as seen above, or as single entries, e.g.:
```json
"typeface": { "path": "data/font/Terminus.ttf", "hinting": "Bitmap" },
"map_typeface": "unifont.ttf"
```
Each entry may be a string or an object. If a string is provided it is interpreted as the path to the typeface file.
If an object is provided, then it must have a path attribute, which provides the path to the font. It may also have
a 'hinting' attribute, which determines the font hinting mode. If it's not specified then 'Default' is used.
Hinting may be one of: Auto, NoAuto, Default, Light, or Bitmap. Antialiasing may also be provided and is default set
to true, but can be disabled by setting to off.

A full object may look like the following:
```json
"gui_typeface": {
"path": "data/font/Roboto-Medium.ttf",
"hinting": "Light",
"antialiasing": true,
}
```

## Hinting

Hinting is the process of modifying the shapes of glyphs so that
they line up better with the pixels in the display device. This
improves readability at low resolutions while potentially changing the
shape of the glyphs. Six different settings are available:

### Default

The default hinter uses tables built into the font by the font
designer. Many fonts look best with this hinter because the font
designer has meticulously crafted the hinting tables so that the font
looks as good as possible.

If the font has no hinting tables at all, then this mode falls back to
using the autohinter.

### Light

Many generated glyphs are fuzzier but better resemble their original
shape. This is achieved by snapping glyphs to the pixel grid only
vertically (Y-axis), as is done by Microsoft's ClearType and Adobe's
proprietary font renderer. This preserves inter-glyph spacing in
horizontal text.

### Auto

This mode ignores the font’s hinting tables in favor of
heuristics. The autohinter produces acceptable results for most fonts,
but may produce terrible results on some fonts. Usually this is only
used when the font doesn’t have any hinting tables of its own.

### NoAuto

This is like the Default setting, but won’t use the autohinter if the
font doesn’t have any hinting tables.

### None

This completely skips the hinting step. This will likely produce
blurry text, but the glyph shapes will always be correct. On very
high resolution monitors the lack of hinting may not be noticable.

### Bitmap

This setting turns off hinting just like None does, but additionally
tells the renderer to ignore any vector glyphs in the font in favor of
bitmapped glyphs. As bitmapped glyphs are never antialiased at all
they will be extremely sharp, but also pixelated. In most contexts the
pixelation would be inappropriate, but it may lend the game a retro
aesthetic that many find appealing. Currently the game defaults to
using the Terminus font with this setting.
36 changes: 20 additions & 16 deletions src/cata_imgui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -365,27 +365,31 @@ static void AddGlyphRangesMisc( UNUSED ImFontGlyphRangesBuilder *b )
b->AddRanges( &superscripts[0] );
}

static void load_font( ImGuiIO &io, const std::vector<std::string> &typefaces,

// Load the first font that exists in typefaces, falling back to unifont
// if none of them exist.
// - typefaces is a list of paths.
static void load_font( ImGuiIO &io, const std::vector<font_config> &typefaces,
const ImWchar *ranges )
{
std::vector<std::string> io_typefaces{ typefaces };
std::vector<font_config> io_typefaces{ typefaces };
ensure_unifont_loaded( io_typefaces );

auto it = std::find_if( io_typefaces.begin(),
io_typefaces.end(),
[]( const std::string & io_typeface ) {
return file_exist( io_typeface );
} );
std::string existing_typeface = *it;
ImFontConfig config = ImFontConfig();
#ifdef IMGUI_ENABLE_FREETYPE
if( existing_typeface.find( "Terminus.ttf" ) != std::string::npos ||
existing_typeface.find( "unifont.ttf" ) != std::string::npos ) {
config.FontBuilderFlags = ImGuiFreeTypeBuilderFlags_ForceAutoHint;
auto it = std::begin( io_typefaces );
for( ; it != std::end( io_typefaces ); ++it ) {
if( !file_exist( it->path ) ) {
debugmsg( "Font file '%s' does not exist.", it->path );
}
break;
}
#endif
if( it == std::end( io_typefaces ) ) {
debugmsg( "No fonts were found in the fontdata file." );
}

ImFontConfig config = ImFontConfig();
config.FontBuilderFlags = it->imgui_config();

io.Fonts->AddFontFromFileTTF( existing_typeface.c_str(), fontheight, &config, ranges );
io.Fonts->AddFontFromFileTTF( it->path.c_str(), fontheight, &config, ranges );
}

static void check_font( const ImFont *font )
Expand All @@ -405,7 +409,7 @@ static void check_font( const ImFont *font )
void cataimgui::client::load_fonts( UNUSED const Font_Ptr &gui_font,
const Font_Ptr &mono_font,
const std::array<SDL_Color, color_loader<SDL_Color>::COLOR_NAMES_COUNT> &windowsPalette,
const std::vector<std::string> &gui_typefaces, const std::vector<std::string> &mono_typefaces )
const std::vector<font_config> &gui_typefaces, const std::vector<font_config> &mono_typefaces )
{
ImGuiIO &io = ImGui::GetIO();
if( ImGui::GetIO().FontDefault == nullptr ) {
Expand Down
4 changes: 3 additions & 1 deletion src/cata_imgui.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include <vector>
#include <unordered_map>

#include "font_loader.h"

class nc_color;
struct input_event;
using ImGuiInputTextFlags = int;
Expand Down Expand Up @@ -74,7 +76,7 @@ class client
const GeometryRenderer_Ptr &sdl_geometry );
void load_fonts( const std::unique_ptr<Font> &gui_font, const std::unique_ptr<Font> &mono_font,
const std::array<SDL_Color, color_loader<SDL_Color>::COLOR_NAMES_COUNT> &windowsPalette,
const std::vector<std::string> &gui_typeface, const std::vector<std::string> &mono_typeface );
const std::vector<font_config> &gui_typeface, const std::vector<font_config> &mono_typeface );
#endif
~client();

Expand Down
Loading
Loading