Avalonia icon indicating copy to clipboard operation
Avalonia copied to clipboard

Symbol font glyphs rendered thicker in avalonia

Open amwx opened this issue 1 year ago • 6 comments

Describe the bug Glyphs in symbol fonts (Segoe Fluent Icons, Segoe MDL2 Assets, etc.) are rendering thicker than they should be in Avalonia (TextBlock/TextLayout and FormattedText). Using Skia in a custom draw op renders them correctly to appear much closer to how they should. SkiaSharp version is whatever ships with Avalonia (I'm not using my own ref here). This also isn't new to the new text engine for 11.0, but I just discovered it's not just an issue with my custom font (based on the open source Fluent UI Icons) but also with the other MS fonts.

1920x1080 display, normal 100% scaling

image

The glyph is \uE8BB from either of the Segoe icon fonts (same as the native close button everywhere on Windows)

Xaml

<Viewbox Width="10"
                 Height="10" 
                 Grid.Column="1">
    <TextBlock Name="ButtonIcon"
               FontFamily="Segoe Fluent Icons"
               FontSize="12"
               Text="&#xE8BB;"
               Foreground="White"/>
</Viewbox>

<Viewbox Width="10"                 
                 Height="10"
                 Grid.Column="1" Grid.Row="1">
    <local:SkiaTextBlock />
</Viewbox>

The Skia CustomDrawOp (SkiaTextBlock)

var tf = SKTypeface.FromFamilyName("Segoe Fluent Icons");

Font = new SKFont
{
    Subpixel = true,
    Edging = SKFontEdging.SubpixelAntialias,
    Hinting = SKFontHinting.Full,
    LinearMetrics = true,
    Typeface = tf,
    Size = 12
};

public void Render(IDrawingContextImpl context)
{
    var api = context.GetFeature<ISkiaSharpApiLeaseFeature>();
    using var lease = api.Lease();

    var dc = lease.SkCanvas;

    var txt = "\uE8BB";
    using var paint = new SKPaint(Font);
    paint.Style = SKPaintStyle.StrokeAndFill;
    paint.Color = SKColors.White;

    SKRect bnds = default;
    paint.MeasureText(txt, ref bnds);
    var cx = (float)Bounds.Center.X;
    var cy = (float)Bounds.Center.Y;

    dc.DrawText(txt, cx - (bnds.Width / 2), cy - (bnds.Height / 2) + bnds.Height, paint);
}

To Reproduce Steps to reproduce the behavior:

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Expected behavior A clear and concise description of what you expected to happen.

Screenshots If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

  • OS: Windows
  • Version 11.0

Additional context Add any other context about the problem here.

amwx avatar Oct 25 '22 20:10 amwx

I would blame the font metrics in the FluentAvalonia.ttf font except you tested others. Very strange. If it does turn out to be something we need to adjust with the font though just let me know. I can take another look at it.

robloo avatar Oct 26 '22 01:10 robloo

I had a deeper look at this and it looks like this is a bug with the SKCanvas.DrawText implementation. If you pass in a string the edging settings are not respected and you don't get a grayscale rendering. If you call this function with a SKTextBlob all settings are respected and you get grayscale rendering etc.

dc.DrawText(glyphRun.TextBlob, cx - (bnds.Width / 2), cy - (bnds.Height / 2) + bnds.Height, paint) //Edging is respected

dc.DrawText(txt, cx - (bnds.Width / 2), cy - (bnds.Height / 2) + bnds.Height, paint) //No edging setting is respected

If I set Edging.Alias both produce the same results

Gillibald avatar Oct 27 '22 16:10 Gillibald

But the expected result is the thinner one with DrawText(string).

But based on what you said I did a quick test and I'm wondering if these symbol fonts aren't meant to be rendered with anti-aliasing, or Skia's anti-aliasing is too much? DirectWrite renders more correctly and I've also noticed that certain foreground-background contrasts can make the glyph appear correct too: image

WPF has the ClearTypeHint property in RenderOptions and the TextOptions class for customizing this behavior. Could this be added (or something similar) to support scenarios like this where aliasing is better?

amwx avatar Oct 27 '22 20:10 amwx

I think I can disable anti-aliasing for symbol fonts

Gillibald avatar Oct 27 '22 20:10 Gillibald

Looking closely at the images I think this is just a difference in anti-aliasing too. DirectWrite simply has a better algorithm in this case but it still looks slightly anti-aliased to me. I wouldn't necessarily just disable antialiasing for Skia in this case... that might create other issues.

I think I can disable anti-aliasing for symbol fonts

How would you detect these? Those fonts that only have glyphs in the private use area?

Because some Microsoft symbol fonts don't follow this rule, others include real characters along with symbol glyphs.

robloo avatar Oct 27 '22 23:10 robloo

Y that is the only option to somehow reliably detect such font. The other option is to provide TextRenderOptions that allow you to disable anti-aliasing in some context. I might just go for implementing TextRenderOptions.

Gillibald avatar Oct 28 '22 09:10 Gillibald