Replace all string parameters in Draw methods by System.ReadOnlySpan<char> to avoid allocations?
See original GitHub issueI’m developing a custom UI system with FontStashSharp. I love this library. Thanks for developing it. It’s so easy to use and so beautiful. And the newest aditions (like IFontStashRenderer2
) are amazing.
I would propose one small but useful change. To swap the string text
parameter in all the Draw overloads by a System.ReadOnlySpan<char>
. This would allow to draw multiline text without extra allocations.
This wouldn’t break any compatibility because string has an implicit conversion to ReadOnlySpan<char>
For example, currently if I want to draw a string with word wraping, I would need to do the following in my TextBlock class:
private string _text = string.Empty;
private List<string> _textLines = new List<string>();
public string Text
{
get => this._text;
set
{
_text = value;
_textLines.Clear();
// Apply word wrapping logic and split the string and put every line inside the text lines list.
// The problem is that every string is immutable, thus generating a new allocation
}
}
protected void Draw()
{
foreach (var line in _textLines)
{
_font.Draw(line, ...etc);
}
}
If the argument type is changed by a ReadOnlySpan, this would allow to apply word wrapping, per word, or per sentence effects without the need of extra allocations.
ReadOnlySpan<char> is a struct (so it does not allocates in the heap) and it is defined as ReadOnlySpan(char[]? array, int start, int length)
. So for a word wrapping system (or something similar) the system would need to create a List<ReadOnlySpan<char>> _textLines
.
For example, a simple WordWrap alogorithm without extra allocations would be like this:
private void WordWrap()
{
this._textLines.Clear();
int firstCharIndex = 0;
int charCount = 0;
for (int i = 0; i < this.Text.Length; i++)
{
charCount++;
if (this.Text[i] == ' ')
{
var span = new ReadOnlySpan<char>(this.Text, firstCharIndex, charCount);
Vector2 size = this._font.MeasureText(span, ...etc);
if (size.X > this.Width)
{
this._textLines.Add(span);
firstCharIndex += charCount + 1;
}
}
}
var span = new ReadOnlySpan<char>(this.Text, firstCharIndex, charCount);
this._textLines.Add(span);
}
Issue Analytics
- State:
- Created a year ago
- Comments:10
well, custom type would let even a BRUTE user to use the new functionality.
Yeah, that’s correct BRUTE. That tool is used to run MG/FNA apps on platforms that don’t have .NET support like Nintendo Switch. AFAIK BRUTE supports maximum “net452”. Hence I’ve chosen “.net standard 2.0”(well, it’s not really “net452”, but closest among targets that arent deprecated yet). BRUTE doesnt support Span in any form. Hence I’ve would have to go with my own type.