osu-framework icon indicating copy to clipboard operation
osu-framework copied to clipboard

Implement CSS-style font size algorithm

Open frenzibyte opened this issue 3 years ago • 9 comments

  • Closes https://github.com/ppy/osu-framework/issues/3271
  • Resolves the font size discrepancy in https://github.com/ppy/osu/issues/17940

Adds support for CSS-style font sizing, allowing for our sprite text sizes to match 1:1 with CSS.

As a starting point, the option is disabled by default to avoid breaking every existing sprite text in all games, but as we move forward (with migrating to new designs in osu!), we'll want to make the option enabled by default, and potentially remove the option (keeping it only based on whether the font's metrics are available).

For reviewing ease, I'll recommend going commit-by-commit as the overall diff is a bit messy to read through (mainly because of the addition of FontMetrics, addition of FontUsage.CssScaling, and addition of note font for testing purposes).

FontMetrics

Since the font binaries don't store metadata about the metrics (ascent, descent, em size), it is required for osu! and all consumers of the framework to manually extract them from the font and specify them in AddFont, similar to how it's done here:

@@ -161,19 +162,24 @@ private void load(FrameworkConfigManager config)
             // note that currently this means there could be two async font load operations.
             Fonts.AddStore(localFonts = new FontStore(useAtlas: false));

+            // Roboto and Font Awesome have different metrics for Windows and macOS.
+            // The ones used for Windows are used here for the time being.
+            var robotoMetrics = new FontMetrics(ascent: 1946, descent: 512, emSize: 2048);
+            var fontAwesomeMetrics = new FontMetrics(ascent: 460, descent: 84, emSize: 512);
+
             // Roboto (FrameworkFont.Regular)
-            addFont(localFonts, Resources, @"Fonts/Roboto/Roboto-Regular");
-            addFont(localFonts, Resources, @"Fonts/Roboto/Roboto-RegularItalic");
-            addFont(localFonts, Resources, @"Fonts/Roboto/Roboto-Bold");
-            addFont(localFonts, Resources, @"Fonts/Roboto/Roboto-BoldItalic");
+            addFont(localFonts, Resources, @"Fonts/Roboto/Roboto-Regular", robotoMetrics);
+            addFont(localFonts, Resources, @"Fonts/Roboto/Roboto-RegularItalic", robotoMetrics);
+            addFont(localFonts, Resources, @"Fonts/Roboto/Roboto-Bold", robotoMetrics);
+            addFont(localFonts, Resources, @"Fonts/Roboto/Roboto-BoldItalic", robotoMetrics);

             // RobotoCondensed (FrameworkFont.Condensed)
-            addFont(localFonts, Resources, @"Fonts/RobotoCondensed/RobotoCondensed-Regular");
-            addFont(localFonts, Resources, @"Fonts/RobotoCondensed/RobotoCondensed-Bold");
+            addFont(localFonts, Resources, @"Fonts/RobotoCondensed/RobotoCondensed-Regular", robotoMetrics);
+            addFont(localFonts, Resources, @"Fonts/RobotoCondensed/RobotoCondensed-Bold", robotoMetrics);

-            addFont(Fonts, Resources, @"Fonts/FontAwesome5/FontAwesome-Solid");
-            addFont(Fonts, Resources, @"Fonts/FontAwesome5/FontAwesome-Regular");
-            addFont(Fonts, Resources, @"Fonts/FontAwesome5/FontAwesome-Brands");
+            addFont(Fonts, Resources, @"Fonts/FontAwesome5/FontAwesome-Solid", fontAwesomeMetrics);
+            addFont(Fonts, Resources, @"Fonts/FontAwesome5/FontAwesome-Regular", fontAwesomeMetrics);
+            addFont(Fonts, Resources, @"Fonts/FontAwesome5/FontAwesome-Brands", fontAwesomeMetrics);

             dependencies.Cache(Fonts);
  • Note that different platforms use different metric tables, and I'm not entirely sure if we should use each metric based on the platform, as it turns out there can be differences. Therefore I continued with the one used by Windows.

    This is still completely up to the consumer whether they want it to be platform-specific or not, but just mentioning this in regards to osu!.

  • I've kept the font metrics argument in AddFont as optional, for special fonts which don't have the concept of "font metrics" ("8-bit" fonts and the like).

I will write up more about this in the fonts wiki if the FontMetrics concept is agreed on, mainly about how to extract them and which values should be used, etc.

FontUsage.CssScaling

Going forward, osu! and all consumers should always append css: true to their font usages, potentially using a different static and prefixing the old non-CSS ones with Old:

diff --git a/osu.Game/Graphics/OsuFont.cs b/osu.Game/Graphics/OsuFont.cs
index edb484021c..62eb7492f8 100644
--- a/osu.Game/Graphics/OsuFont.cs
+++ b/osu.Game/Graphics/OsuFont.cs
@@ -19,7 +19,9 @@ public static class OsuFont

         public static FontUsage Numeric => GetFont(Typeface.Venera, weight: FontWeight.Bold);

-        public static FontUsage Torus => GetFont(Typeface.Torus, weight: FontWeight.Regular);
+        public static FontUsage OldTorus => GetFont(Typeface.Torus, weight: FontWeight.Regular);
+
+        public static FontUsage Torus => GetFont(Typeface.Torus, weight: FontWeight.Regular, css: true);

         public static FontUsage TorusAlternate => GetFont(Typeface.TorusAlternate, weight: FontWeight.Regular);

It could also go the other way around, by keeping the old one as-is, and defining a New font with css: true applied instead.


I've added behavioural test coverage, and also added a visual illustration in the sprite text test scene, by comparing against a picture of a CSS text element with an equal font size, in a properly aligned container:

CleanShot 2022-04-23 at 06 48 29@2x

Have also added sprite texts with CSS-style enabled, alongside the pre-existing non-CSS ones:

CleanShot 2022-04-23 at 06 50 43@2x

frenzibyte avatar Apr 23 '22 04:04 frenzibyte

and addition of noto font for testing purposes

Why was this done? I think it would have sufficed to test Roboto.

bdach avatar Apr 23 '22 07:04 bdach

Just for the sake of adding more into the visualisation test, and also to confirm that the framework’s handling of multiple fonts in one sprite text also matches CSS.

frenzibyte avatar Apr 23 '22 07:04 frenzibyte

The test doesn't seem to be working for me: image

I don't seem to have any local changes, so hopefully it's not just me?

smoogipoo avatar May 10 '22 09:05 smoogipoo

Indeed it regressed, will look into this immediately...

frenzibyte avatar May 10 '22 10:05 frenzibyte

Apparently the test scene regressed because of https://github.com/ppy/osu-framework/pull/5123/commits/78d32b636864d29903c976fb319fda4b2f3c80e9 and I swear it wasn't broken to me the moment I tested it...

That commit was changing the metrics values to use the ones which Windows respects (typo), but the text images provided were taken on macOS (hhea), therefore causing this discrepancy.

The metrics provided by Windows lead to CSS scale of 1x ((880 + 120) / 1000 = 1), so I'm going to stick with the macOS ones with an inline comment and call it a day.

frenzibyte avatar May 10 '22 10:05 frenzibyte

That commit was changing the metrics values to use the ones which Windows respects (typo), but the text images provided were taken on macOS (hhea)

Well this is disconcerting, are you saying this can differ per-platform somehow, too? I would hope not...

bdach avatar May 10 '22 14:05 bdach

Well, some fonts render differently on browsers in Windows and macOS, due to there existing different metrics tables (most of the time the same values will be used on all metrics, but some fonts like Roboto and Noto Sans JP don't):

It is up for the framework consumer to decide whether they want their font to be rendered correctly based on the running platform (using RuntimeInfo.OS), or by sticking to one metrics table and call it a day.

For osu!, I think it may be best sticking to the Typo table, but we can consider rendering per-platform correctly if we want to. Just a matter of supplying the font with the correct metric values.

frenzibyte avatar May 10 '22 14:05 frenzibyte

I think I'm just going to pretend I didn't ask and nod along. This is a rabbit hole the depths of which I do not dare to explore.

bdach avatar May 10 '22 14:05 bdach

For reference I found the relevant platform specific hacks that Chrome uses: https://github.com/chromium/chromium/blob/76769a9d7e911b4efa1fab3cc6e1b5df86f3d227/third_party/blink/renderer/platform/fonts/simple_font_data.cc#L88-L179

jai-x avatar May 10 '22 14:05 jai-x