diff --git a/engine/Sandbox.Engine/Editor/Gizmos/Draw/Draw.cs b/engine/Sandbox.Engine/Editor/Gizmos/Draw/Draw.cs
index d6595692..e617c0cd 100644
--- a/engine/Sandbox.Engine/Editor/Gizmos/Draw/Draw.cs
+++ b/engine/Sandbox.Engine/Editor/Gizmos/Draw/Draw.cs
@@ -209,6 +209,20 @@ public static partial class Gizmo
so.TextFlags = flags;
}
+ ///
+ /// Draw text with a text rendering scope for more text rendering customization.
+ ///
+ 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;
+ }
+
///
/// Draw text on screen at a 3d position
///
@@ -220,6 +234,17 @@ public static partial class Gizmo
ScreenText( text, screen + offset, font, size, flags );
}
+ ///
+ /// Draw text on screen at a 3d position with a text rendering scope for more text rendering customization.
+ ///
+ 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 );
+ }
+
///
/// Draw a rect, on the screen
///
diff --git a/engine/Sandbox.Engine/Resources/Textures/Bitmap/Bitmap.Text.cs b/engine/Sandbox.Engine/Resources/Textures/Bitmap/Bitmap.Text.cs
index 343a9326..023a87cc 100644
--- a/engine/Sandbox.Engine/Resources/Textures/Bitmap/Bitmap.Text.cs
+++ b/engine/Sandbox.Engine/Resources/Textures/Bitmap/Bitmap.Text.cs
@@ -9,7 +9,7 @@ public partial class Bitmap
///
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 );
}
}
diff --git a/engine/Sandbox.Engine/Systems/Render/TextRendering/TextRendering.Scope.cs b/engine/Sandbox.Engine/Systems/Render/TextRendering/TextRendering.Scope.cs
index 9b4e1e54..35e43d78 100644
--- a/engine/Sandbox.Engine/Systems/Render/TextRendering/TextRendering.Scope.cs
+++ b/engine/Sandbox.Engine/Systems/Render/TextRendering/TextRendering.Scope.cs
@@ -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();
}
diff --git a/engine/Sandbox.Engine/Systems/Render/TextRendering/TextRendering.TextBlock.cs b/engine/Sandbox.Engine/Systems/Render/TextRendering/TextRendering.TextBlock.cs
index 20d05e8f..9e28ab63 100644
--- a/engine/Sandbox.Engine/Systems/Render/TextRendering/TextRendering.TextBlock.cs
+++ b/engine/Sandbox.Engine/Systems/Render/TextRendering/TextRendering.TextBlock.cs
@@ -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,
diff --git a/engine/Sandbox.Engine/Systems/Render/TextRendering/TextRendering.cs b/engine/Sandbox.Engine/Systems/Render/TextRendering/TextRendering.cs
index 9ccd4d99..92d04801 100644
--- a/engine/Sandbox.Engine/Systems/Render/TextRendering/TextRendering.cs
+++ b/engine/Sandbox.Engine/Systems/Render/TextRendering/TextRendering.cs
@@ -20,7 +20,7 @@ public static partial class TextRendering
///
/// Create a texture from the scope. The texture will either be a cached version or will be rendered immediately
///
- 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 );
}
diff --git a/engine/ThirdParty/Topten.RichTextKit/FontRun.cs b/engine/ThirdParty/Topten.RichTextKit/FontRun.cs
index 8cb536f7..f683c548 100644
--- a/engine/ThirdParty/Topten.RichTextKit/FontRun.cs
+++ b/engine/ThirdParty/Topten.RichTextKit/FontRun.cs
@@ -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;