mirror of
https://github.com/Facepunch/sbox-public.git
synced 2025-12-23 14:38:13 -05:00
Text rendering scope font smooth & 1 px aliased outline support (#3642) https://files.facepunch.com/laylad/1b1811b1/sbox-dev_CExpJeBiL5.png
This commit is contained in:
@@ -209,6 +209,20 @@ public static partial class Gizmo
|
||||
so.TextFlags = flags;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw text with a text rendering scope for more text rendering customization.
|
||||
/// </summary>
|
||||
public void ScreenText( TextRendering.Scope text, Vector2 pos, TextFlag flags = TextFlag.LeftTop )
|
||||
{
|
||||
var so = Active.FindOrCreate( $"text", () => new TextSceneObject( World ) );
|
||||
|
||||
so.TextBlock = text;
|
||||
so.Transform = Transform.Zero;
|
||||
so.ScreenPos = pos;
|
||||
so.Bounds = BBox.FromPositionAndSize( 0, float.MaxValue );
|
||||
so.TextFlags = flags;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw text on screen at a 3d position
|
||||
/// </summary>
|
||||
@@ -220,6 +234,17 @@ public static partial class Gizmo
|
||||
ScreenText( text, screen + offset, font, size, flags );
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw text on screen at a 3d position with a text rendering scope for more text rendering customization.
|
||||
/// </summary>
|
||||
public void ScreenText( TextRendering.Scope text, Vector3 worldPos, Vector2 offset, TextFlag flags = TextFlag.LeftTop )
|
||||
{
|
||||
if ( !Camera.ToScreen( worldPos, out var screen ) )
|
||||
return;
|
||||
|
||||
ScreenText( text, screen + offset, flags );
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw a rect, on the screen
|
||||
/// </summary>
|
||||
|
||||
@@ -9,7 +9,7 @@ public partial class Bitmap
|
||||
/// </summary>
|
||||
public void DrawText( TextRendering.Scope scope, Rect rect, TextFlag flags = TextFlag.Center )
|
||||
{
|
||||
using var block = new TextRendering.TextBlock( scope, new Vector2( 0, 0 ), flags, FontSmooth.Always );
|
||||
using var block = new TextRendering.TextBlock( scope, new Vector2( 0, 0 ), flags );
|
||||
block.Render( _canvas, rect );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ public static partial class TextRendering
|
||||
[JsonInclude] public float WordSpacing;
|
||||
|
||||
[JsonInclude] public Rendering.FilterMode FilterMode;
|
||||
[JsonInclude] public UI.FontSmooth FontSmooth;
|
||||
|
||||
// this seems stupid, but it's like this because a list would be a pain
|
||||
// and also would be a bunch of bullshit to hashcode. This is fast.
|
||||
@@ -67,6 +68,7 @@ public static partial class TextRendering
|
||||
hc.Add( Shadow );
|
||||
hc.Add( ShadowUnder );
|
||||
hc.Add( FilterMode );
|
||||
hc.Add( FontSmooth );
|
||||
|
||||
return hc.ToHashCode();
|
||||
}
|
||||
|
||||
@@ -14,47 +14,31 @@ public static partial class TextRendering
|
||||
|
||||
public TextFlag Flags;
|
||||
public Vector2 Clip;
|
||||
public FontSmooth Smooth;
|
||||
public bool IsEmpty;
|
||||
|
||||
public RealTimeSince TimeSinceUsed;
|
||||
|
||||
TextRendering.Scope Scope;
|
||||
|
||||
|
||||
Scope _scope;
|
||||
Margin _effectMargin = default;
|
||||
|
||||
public TextBlock( string text, Color color, string font, float size, int fontWeight, Vector2 clip, TextFlag flag, FontSmooth? smooth = FontSmooth.Auto )
|
||||
public TextBlock( Scope scope, Vector2 clip, TextFlag flag )
|
||||
{
|
||||
Assert.False( Application.IsHeadless );
|
||||
|
||||
Flags = flag;
|
||||
Clip = clip;
|
||||
Smooth = smooth ?? FontSmooth.Auto;
|
||||
|
||||
Initialize( new TextRendering.Scope { Text = text, TextColor = color, FontName = font, FontSize = size, FontWeight = fontWeight } );
|
||||
}
|
||||
|
||||
public TextBlock( Scope scope, Vector2 clip, TextFlag flag, FontSmooth? smooth = FontSmooth.Auto )
|
||||
{
|
||||
Assert.False( Application.IsHeadless );
|
||||
|
||||
Flags = flag;
|
||||
Clip = clip;
|
||||
Smooth = smooth ?? FontSmooth.Auto;
|
||||
|
||||
Initialize( scope );
|
||||
}
|
||||
|
||||
public TextBlock()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
internal void Initialize( TextRendering.Scope scope )
|
||||
internal void Initialize( Scope scope )
|
||||
{
|
||||
Scope = scope;
|
||||
IsEmpty = string.IsNullOrEmpty( Scope.Text );
|
||||
_scope = scope;
|
||||
IsEmpty = string.IsNullOrEmpty( _scope.Text );
|
||||
|
||||
_effectMargin = default;
|
||||
|
||||
@@ -134,13 +118,13 @@ public static partial class TextRendering
|
||||
}
|
||||
|
||||
var style = new Topten.RichTextKit.Style();
|
||||
Scope.ToStyle( style );
|
||||
_scope.ToStyle( style );
|
||||
|
||||
block.AddText( Scope.Text, style );
|
||||
block.AddText( _scope.Text, style );
|
||||
|
||||
var o = new Topten.RichTextKit.TextPaintOptions
|
||||
{
|
||||
Edging = Smooth switch
|
||||
Edging = _scope.FontSmooth switch
|
||||
{
|
||||
FontSmooth.Never => SKFontEdging.Alias,
|
||||
_ => SKFontEdging.Antialias,
|
||||
@@ -190,9 +174,9 @@ public static partial class TextRendering
|
||||
// Build text block
|
||||
//
|
||||
var style = new Topten.RichTextKit.Style();
|
||||
Scope.ToStyle( style );
|
||||
_scope.ToStyle( style );
|
||||
|
||||
block.AddText( IsEmpty ? "." : Scope.Text, style );
|
||||
block.AddText( IsEmpty ? "." : _scope.Text, style );
|
||||
|
||||
//
|
||||
// Build Text
|
||||
@@ -215,7 +199,7 @@ public static partial class TextRendering
|
||||
{
|
||||
var o = new Topten.RichTextKit.TextPaintOptions
|
||||
{
|
||||
Edging = Smooth switch
|
||||
Edging = _scope.FontSmooth switch
|
||||
{
|
||||
FontSmooth.Never => SKFontEdging.Alias,
|
||||
_ => SKFontEdging.Antialias,
|
||||
|
||||
@@ -20,7 +20,7 @@ public static partial class TextRendering
|
||||
/// <summary>
|
||||
/// Create a texture from the scope. The texture will either be a cached version or will be rendered immediately
|
||||
/// </summary>
|
||||
public static Texture GetOrCreateTexture( in Scope scope, Vector2 clip = default, TextFlag flag = TextFlag.LeftTop, FontSmooth smooth = FontSmooth.Auto )
|
||||
public static Texture GetOrCreateTexture( in Scope scope, Vector2 clip = default, TextFlag flag = TextFlag.LeftTop )
|
||||
{
|
||||
if ( Application.IsHeadless )
|
||||
return Texture.Invalid;
|
||||
@@ -31,7 +31,6 @@ public static partial class TextRendering
|
||||
hc.Add( scope );
|
||||
hc.Add( clip );
|
||||
hc.Add( flag );
|
||||
hc.Add( smooth );
|
||||
|
||||
// TextManager is caching this right now
|
||||
var tb = GetOrCreateTextBlock( hc.ToHashCode(), out bool created );
|
||||
@@ -40,7 +39,6 @@ public static partial class TextRendering
|
||||
{
|
||||
tb.Clip = clip;
|
||||
tb.Flags = flag;
|
||||
tb.Smooth = FontSmooth.Auto;
|
||||
tb.Initialize( scope );
|
||||
}
|
||||
|
||||
|
||||
56
engine/ThirdParty/Topten.RichTextKit/FontRun.cs
vendored
56
engine/ThirdParty/Topten.RichTextKit/FontRun.cs
vendored
@@ -538,9 +538,6 @@ namespace Topten.RichTextKit
|
||||
glyphVOffset = Style.FontSize * 0.1f;
|
||||
}
|
||||
|
||||
// Get glyph positions
|
||||
var glyphPositions = GlyphPositions.ToArray();
|
||||
|
||||
// Create the font
|
||||
if ( _font == null )
|
||||
{
|
||||
@@ -872,6 +869,16 @@ namespace Topten.RichTextKit
|
||||
using var effectPaint = new SKPaint();
|
||||
foreach ( var effect in Style.TextEffects )
|
||||
{
|
||||
// Aliased 1 pixel outline needs to be rendered in a different way to look good.
|
||||
if ( ctx.Options.Edging == SKFontEdging.Alias &&
|
||||
effect.PaintStyle == SKPaintStyle.StrokeAndFill &&
|
||||
effect.Width == 1 )
|
||||
{
|
||||
PaintPixelOutline( ctx, effect.Color );
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
effectPaint.Style = effect.PaintStyle;
|
||||
effectPaint.StrokeWidth = effect.Width;
|
||||
effectPaint.StrokeJoin = effect.StrokeJoin;
|
||||
@@ -885,6 +892,49 @@ namespace Topten.RichTextKit
|
||||
}
|
||||
}
|
||||
|
||||
static readonly SKPoint[] PixelOutlineOffsets =
|
||||
{
|
||||
new( -1, 0 ),
|
||||
new( 1, 0 ),
|
||||
new( 0, -1 ),
|
||||
new( 0, 1 ),
|
||||
new( -1, -1 ),
|
||||
new( -1, 1 ),
|
||||
new( 1, -1 ),
|
||||
new( 1, 1 ),
|
||||
};
|
||||
|
||||
unsafe void PaintPixelOutline( PaintTextContext ctx, SKColor color )
|
||||
{
|
||||
using var pixelPaint = new SKPaint
|
||||
{
|
||||
Color = color,
|
||||
IsAntialias = false,
|
||||
};
|
||||
|
||||
// Override font edging.
|
||||
var edging = _font.Edging;
|
||||
_font.Edging = SKFontEdging.Alias;
|
||||
|
||||
fixed ( ushort* pGlyphs = Glyphs.Underlying )
|
||||
{
|
||||
var textBlob = SKTextBlob.CreatePositioned(
|
||||
(IntPtr)(pGlyphs + Glyphs.Start),
|
||||
Glyphs.Length * sizeof( ushort ),
|
||||
SKTextEncoding.GlyphId,
|
||||
_font,
|
||||
GlyphPositions.AsSpan() );
|
||||
|
||||
foreach ( var o in PixelOutlineOffsets )
|
||||
{
|
||||
ctx.Canvas.DrawText( textBlob, o.X, o.Y, pixelPaint );
|
||||
}
|
||||
}
|
||||
|
||||
// Restore font edging.
|
||||
_font.Edging = edging;
|
||||
}
|
||||
|
||||
SKTextBlob _textBlob;
|
||||
SKFont _font;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user