|
@@ -123,11 +123,31 @@ public class TextFormatter
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
string strings = linesFormatted [line];
|
|
string strings = linesFormatted [line];
|
|
|
- string[] graphemes = GraphemeHelper.GetGraphemes (strings).ToArray ();
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // Use ArrayPool to avoid per-draw allocations
|
|
|
|
|
+ int estimatedCount = strings.Length + 10; // Add buffer for grapheme clusters
|
|
|
|
|
+ string [] graphemes = ArrayPool<string>.Shared.Rent (estimatedCount);
|
|
|
|
|
+ var graphemeCount = 0;
|
|
|
|
|
|
|
|
- // When text is justified, we lost left or right, so we use the direction to align.
|
|
|
|
|
|
|
+ try
|
|
|
|
|
+ {
|
|
|
|
|
+ foreach (string grapheme in GraphemeHelper.GetGraphemes (strings))
|
|
|
|
|
+ {
|
|
|
|
|
+ if (graphemeCount >= graphemes.Length)
|
|
|
|
|
+ {
|
|
|
|
|
+ // Need larger array (rare case for complex text)
|
|
|
|
|
+ string [] larger = ArrayPool<string>.Shared.Rent (graphemes.Length * 2);
|
|
|
|
|
+ Array.Copy (graphemes, larger, graphemeCount);
|
|
|
|
|
+ ArrayPool<string>.Shared.Return (graphemes, clearArray: true);
|
|
|
|
|
+ graphemes = larger;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ graphemes [graphemeCount++] = grapheme;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // When text is justified, we lost left or right, so we use the direction to align.
|
|
|
|
|
|
|
|
- int x = 0, y = 0;
|
|
|
|
|
|
|
+ int x = 0, y = 0;
|
|
|
|
|
|
|
|
// Horizontal Alignment
|
|
// Horizontal Alignment
|
|
|
if (Alignment is Alignment.End)
|
|
if (Alignment is Alignment.End)
|
|
@@ -214,7 +234,7 @@ public class TextFormatter
|
|
|
{
|
|
{
|
|
|
if (isVertical)
|
|
if (isVertical)
|
|
|
{
|
|
{
|
|
|
- y = screen.Bottom - graphemes.Length;
|
|
|
|
|
|
|
+ y = screen.Bottom - graphemeCount;
|
|
|
}
|
|
}
|
|
|
else
|
|
else
|
|
|
{
|
|
{
|
|
@@ -250,7 +270,7 @@ public class TextFormatter
|
|
|
{
|
|
{
|
|
|
if (isVertical)
|
|
if (isVertical)
|
|
|
{
|
|
{
|
|
|
- int s = (screen.Height - graphemes.Length) / 2;
|
|
|
|
|
|
|
+ int s = (screen.Height - graphemeCount) / 2;
|
|
|
y = screen.Top + s;
|
|
y = screen.Top + s;
|
|
|
}
|
|
}
|
|
|
else
|
|
else
|
|
@@ -292,17 +312,17 @@ public class TextFormatter
|
|
|
continue;
|
|
continue;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (!FillRemaining && idx > graphemes.Length - 1)
|
|
|
|
|
|
|
+ if (!FillRemaining && idx > graphemeCount - 1)
|
|
|
{
|
|
{
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if ((!isVertical
|
|
if ((!isVertical
|
|
|
&& (current - start > maxScreen.Left + maxScreen.Width - screen.X + colOffset
|
|
&& (current - start > maxScreen.Left + maxScreen.Width - screen.X + colOffset
|
|
|
- || (idx < graphemes.Length && graphemes [idx].GetColumns () > screen.Width)))
|
|
|
|
|
|
|
+ || (idx < graphemeCount && graphemes [idx].GetColumns () > screen.Width)))
|
|
|
|| (isVertical
|
|
|| (isVertical
|
|
|
&& ((current > start + size + zeroLengthCount && idx > maxScreen.Top + maxScreen.Height - screen.Y)
|
|
&& ((current > start + size + zeroLengthCount && idx > maxScreen.Top + maxScreen.Height - screen.Y)
|
|
|
- || (idx < graphemes.Length && graphemes [idx].GetColumns () > screen.Width))))
|
|
|
|
|
|
|
+ || (idx < graphemeCount && graphemes [idx].GetColumns () > screen.Width))))
|
|
|
{
|
|
{
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
@@ -317,7 +337,7 @@ public class TextFormatter
|
|
|
|
|
|
|
|
if (isVertical)
|
|
if (isVertical)
|
|
|
{
|
|
{
|
|
|
- if (idx >= 0 && idx < graphemes.Length)
|
|
|
|
|
|
|
+ if (idx >= 0 && idx < graphemeCount)
|
|
|
{
|
|
{
|
|
|
text = graphemes [idx];
|
|
text = graphemes [idx];
|
|
|
}
|
|
}
|
|
@@ -368,7 +388,7 @@ public class TextFormatter
|
|
|
{
|
|
{
|
|
|
driver?.Move (current, y);
|
|
driver?.Move (current, y);
|
|
|
|
|
|
|
|
- if (idx >= 0 && idx < graphemes.Length)
|
|
|
|
|
|
|
+ if (idx >= 0 && idx < graphemeCount)
|
|
|
{
|
|
{
|
|
|
text = graphemes [idx];
|
|
text = graphemes [idx];
|
|
|
}
|
|
}
|
|
@@ -428,15 +448,20 @@ public class TextFormatter
|
|
|
current += runeWidth;
|
|
current += runeWidth;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- int nextRuneWidth = idx + 1 > -1 && idx + 1 < graphemes.Length
|
|
|
|
|
|
|
+ int nextRuneWidth = idx + 1 > -1 && idx + 1 < graphemeCount
|
|
|
? graphemes [idx + 1].GetColumns ()
|
|
? graphemes [idx + 1].GetColumns ()
|
|
|
: 0;
|
|
: 0;
|
|
|
|
|
|
|
|
- if (!isVertical && idx + 1 < graphemes.Length && current + nextRuneWidth > start + size)
|
|
|
|
|
|
|
+ if (!isVertical && idx + 1 < graphemeCount && current + nextRuneWidth > start + size)
|
|
|
{
|
|
{
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+ }
|
|
|
|
|
+ finally
|
|
|
|
|
+ {
|
|
|
|
|
+ ArrayPool<string>.Shared.Return (graphemes, clearArray: true);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -931,10 +956,30 @@ public class TextFormatter
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
string strings = linesFormatted [line];
|
|
string strings = linesFormatted [line];
|
|
|
- string [] graphemes = GraphemeHelper.GetGraphemes (strings).ToArray ();
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // Use ArrayPool to avoid per-line allocations
|
|
|
|
|
+ int estimatedCount = strings.Length + 10; // Add buffer for grapheme clusters
|
|
|
|
|
+ string [] graphemes = ArrayPool<string>.Shared.Rent (estimatedCount);
|
|
|
|
|
+ var graphemeCount = 0;
|
|
|
|
|
|
|
|
- // When text is justified, we lost left or right, so we use the direction to align.
|
|
|
|
|
- int x = 0, y = 0;
|
|
|
|
|
|
|
+ try
|
|
|
|
|
+ {
|
|
|
|
|
+ foreach (string grapheme in GraphemeHelper.GetGraphemes (strings))
|
|
|
|
|
+ {
|
|
|
|
|
+ if (graphemeCount >= graphemes.Length)
|
|
|
|
|
+ {
|
|
|
|
|
+ // Need larger array (rare case for complex text)
|
|
|
|
|
+ string [] larger = ArrayPool<string>.Shared.Rent (graphemes.Length * 2);
|
|
|
|
|
+ Array.Copy (graphemes, larger, graphemeCount);
|
|
|
|
|
+ ArrayPool<string>.Shared.Return (graphemes, clearArray: true);
|
|
|
|
|
+ graphemes = larger;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ graphemes [graphemeCount++] = grapheme;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // When text is justified, we lost left or right, so we use the direction to align.
|
|
|
|
|
+ int x = 0, y = 0;
|
|
|
|
|
|
|
|
switch (Alignment)
|
|
switch (Alignment)
|
|
|
{
|
|
{
|
|
@@ -1011,7 +1056,7 @@ public class TextFormatter
|
|
|
{
|
|
{
|
|
|
// Vertical Alignment
|
|
// Vertical Alignment
|
|
|
case Alignment.End when isVertical:
|
|
case Alignment.End when isVertical:
|
|
|
- y = screen.Bottom - graphemes.Length;
|
|
|
|
|
|
|
+ y = screen.Bottom - graphemeCount;
|
|
|
|
|
|
|
|
break;
|
|
break;
|
|
|
case Alignment.End:
|
|
case Alignment.End:
|
|
@@ -1041,7 +1086,7 @@ public class TextFormatter
|
|
|
}
|
|
}
|
|
|
case Alignment.Center when isVertical:
|
|
case Alignment.Center when isVertical:
|
|
|
{
|
|
{
|
|
|
- int s = (screen.Height - graphemes.Length) / 2;
|
|
|
|
|
|
|
+ int s = (screen.Height - graphemeCount) / 2;
|
|
|
y = screen.Top + s;
|
|
y = screen.Top + s;
|
|
|
|
|
|
|
|
break;
|
|
break;
|
|
@@ -1081,22 +1126,22 @@ public class TextFormatter
|
|
|
continue;
|
|
continue;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (!FillRemaining && idx > graphemes.Length - 1)
|
|
|
|
|
|
|
+ if (!FillRemaining && idx > graphemeCount - 1)
|
|
|
{
|
|
{
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if ((!isVertical
|
|
if ((!isVertical
|
|
|
&& (current - start > maxScreen.Left + maxScreen.Width - screen.X + colOffset
|
|
&& (current - start > maxScreen.Left + maxScreen.Width - screen.X + colOffset
|
|
|
- || (idx < graphemes.Length && graphemes [idx].GetColumns () > screen.Width)))
|
|
|
|
|
|
|
+ || (idx < graphemeCount && graphemes [idx].GetColumns () > screen.Width)))
|
|
|
|| (isVertical
|
|
|| (isVertical
|
|
|
&& ((current > start + size + zeroLengthCount && idx > maxScreen.Top + maxScreen.Height - screen.Y)
|
|
&& ((current > start + size + zeroLengthCount && idx > maxScreen.Top + maxScreen.Height - screen.Y)
|
|
|
- || (idx < graphemes.Length && graphemes [idx].GetColumns () > screen.Width))))
|
|
|
|
|
|
|
+ || (idx < graphemeCount && graphemes [idx].GetColumns () > screen.Width))))
|
|
|
{
|
|
{
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- string text = idx >= 0 && idx < graphemes.Length ? graphemes [idx] : " ";
|
|
|
|
|
|
|
+ string text = idx >= 0 && idx < graphemeCount ? graphemes [idx] : " ";
|
|
|
int runeWidth = GetStringWidth (text, TabWidth);
|
|
int runeWidth = GetStringWidth (text, TabWidth);
|
|
|
|
|
|
|
|
if (isVertical)
|
|
if (isVertical)
|
|
@@ -1116,20 +1161,25 @@ public class TextFormatter
|
|
|
|
|
|
|
|
current += isVertical && runeWidth > 0 ? 1 : runeWidth;
|
|
current += isVertical && runeWidth > 0 ? 1 : runeWidth;
|
|
|
|
|
|
|
|
- int nextStringWidth = idx + 1 > -1 && idx + 1 < graphemes.Length
|
|
|
|
|
|
|
+ int nextStringWidth = idx + 1 > -1 && idx + 1 < graphemeCount
|
|
|
? graphemes [idx + 1].GetColumns ()
|
|
? graphemes [idx + 1].GetColumns ()
|
|
|
: 0;
|
|
: 0;
|
|
|
|
|
|
|
|
- if (!isVertical && idx + 1 < graphemes.Length && current + nextStringWidth > start + size)
|
|
|
|
|
|
|
+ if (!isVertical && idx + 1 < graphemeCount && current + nextStringWidth > start + size)
|
|
|
{
|
|
{
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Add the line's drawn region to the overall region
|
|
|
|
|
- if (lineWidth > 0 && lineHeight > 0)
|
|
|
|
|
|
|
+ // Add the line's drawn region to the overall region
|
|
|
|
|
+ if (lineWidth > 0 && lineHeight > 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ drawnRegion.Union (new Rectangle (lineX, lineY, lineWidth, lineHeight));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ finally
|
|
|
{
|
|
{
|
|
|
- drawnRegion.Union (new Rectangle (lineX, lineY, lineWidth, lineHeight));
|
|
|
|
|
|
|
+ ArrayPool<string>.Shared.Return (graphemes, clearArray: true);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|