@@ -1182,42 +1182,40 @@ VerticalOrientationType.Rotate or
11821182 lineBreaks . Add ( lineBreakEnumerator . Current ) ;
11831183 }
11841184
1185- int usedOffset = 0 ;
1185+ int processed = 0 ;
11861186 while ( textLine . Count > 0 )
11871187 {
11881188 LineBreak ? bestBreak = null ;
11891189 foreach ( LineBreak lineBreak in lineBreaks )
11901190 {
1191- // Adjust the break index relative to the current position in the original line
1192- int measureAt = lineBreak . PositionMeasure - usedOffset ;
1193-
1194- // Skip breaks that are already behind the trimmed portion
1195- if ( measureAt < 0 )
1191+ // Skip breaks that are already behind the processed portion
1192+ if ( lineBreak . PositionWrap <= processed )
11961193 {
11971194 continue ;
11981195 }
11991196
12001197 // Measure the text up to the adjusted break point
1201- float measure = textLine . MeasureAt ( measureAt ) ;
1202- if ( measure > wrappingLength )
1198+ float advance = textLine . MeasureAt ( lineBreak . PositionMeasure - processed ) ;
1199+ if ( advance >= wrappingLength )
12031200 {
1204- // Stop and use the best break so far
12051201 bestBreak ??= lineBreak ;
12061202 break ;
12071203 }
12081204
1209- // Update the best break
1210- bestBreak = lineBreak ;
1211-
12121205 // If it's a mandatory break, stop immediately
12131206 if ( lineBreak . Required )
12141207 {
1208+ bestBreak = lineBreak ;
12151209 break ;
12161210 }
1211+
1212+ // Update the best break
1213+ bestBreak = lineBreak ;
12171214 }
12181215
12191216 if ( bestBreak != null )
12201217 {
1218+ LineBreak breakAt = bestBreak . Value ;
12211219 if ( breakAll )
12221220 {
12231221 // Break-all works differently to the other modes.
@@ -1226,30 +1224,34 @@ VerticalOrientationType.Rotate or
12261224 TextLine ? remaining ;
12271225 if ( bestBreak . Value . Required )
12281226 {
1229- if ( textLine . TrySplitAt ( bestBreak . Value , keepAll , out remaining ) )
1227+ if ( textLine . TrySplitAt ( breakAt , keepAll , out remaining ) )
12301228 {
1231- usedOffset += textLine . Count ;
1229+ processed = breakAt . PositionWrap ;
12321230 textLines . Add ( textLine . Finalize ( options ) ) ;
12331231 textLine = remaining ;
12341232 }
12351233 }
12361234 else if ( textLine . TrySplitAt ( wrappingLength , out remaining ) )
12371235 {
1238- usedOffset += textLine . Count ;
1236+ processed += textLine . Count ;
12391237 textLines . Add ( textLine . Finalize ( options ) ) ;
12401238 textLine = remaining ;
12411239 }
12421240 else
12431241 {
1244- usedOffset += textLine . Count ;
1242+ processed += textLine . Count ;
12451243 }
12461244 }
12471245 else
12481246 {
12491247 // Split the current line at the adjusted break index
1250- if ( textLine . TrySplitAt ( bestBreak . Value , keepAll , out TextLine ? remaining ) )
1248+ if ( textLine . TrySplitAt ( breakAt , keepAll , out TextLine ? remaining ) )
12511249 {
1252- usedOffset += textLine . Count ;
1250+ // If 'keepAll' is true then the break could be later than expected.
1251+ processed = keepAll
1252+ ? processed + Math . Max ( textLine . Count , breakAt . PositionWrap - processed )
1253+ : breakAt . PositionWrap ;
1254+
12531255 if ( breakWord )
12541256 {
12551257 // A break was found, but we need to check if the line is too long
@@ -1258,7 +1260,7 @@ VerticalOrientationType.Rotate or
12581260 textLine . TrySplitAt ( wrappingLength , out TextLine ? overflow ) )
12591261 {
12601262 // Reinsert the overflow at the beginning of the remaining line
1261- usedOffset -= overflow . Count ;
1263+ processed -= overflow . Count ;
12621264 remaining . InsertAt ( 0 , overflow ) ;
12631265 }
12641266 }
@@ -1269,13 +1271,14 @@ VerticalOrientationType.Rotate or
12691271 }
12701272 else
12711273 {
1272- usedOffset += textLine . Count ;
1274+ processed += textLine . Count ;
12731275 }
12741276 }
12751277 }
12761278 else
12771279 {
1278- // If no valid break is found, add the remaining line and exit
1280+ // We're at the last line break which should be at the end of the
1281+ // text. We can break here and finalize the line.
12791282 if ( breakWord || breakAll )
12801283 {
12811284 while ( textLine . ScaledLineAdvance > wrappingLength )
@@ -1316,6 +1319,7 @@ public float ScaledMaxAdvance()
13161319 internal sealed class TextLine
13171320 {
13181321 private readonly List < GlyphLayoutData > data ;
1322+ private readonly Dictionary < int , float > advances = new ( ) ;
13191323
13201324 public TextLine ( ) => this . data = new ( 16 ) ;
13211325
@@ -1383,6 +1387,11 @@ public void InsertAt(int index, TextLine textLine)
13831387
13841388 public float MeasureAt ( int index )
13851389 {
1390+ if ( this . advances . TryGetValue ( index , out float advance ) )
1391+ {
1392+ return advance ;
1393+ }
1394+
13861395 if ( index >= this . data . Count )
13871396 {
13881397 index = this . data . Count - 1 ;
@@ -1395,12 +1404,13 @@ public float MeasureAt(int index)
13951404 index -- ;
13961405 }
13971406
1398- float advance = 0 ;
1407+ advance = 0 ;
13991408 for ( int i = 0 ; i <= index ; i ++ )
14001409 {
14011410 advance += this . data [ i ] . ScaledAdvance ;
14021411 }
14031412
1413+ this . advances [ index ] = advance ;
14041414 return advance ;
14051415 }
14061416
@@ -1440,11 +1450,11 @@ public bool TrySplitAt(float length, [NotNullWhen(true)] out TextLine? result)
14401450 public bool TrySplitAt ( LineBreak lineBreak , bool keepAll , [ NotNullWhen ( true ) ] out TextLine ? result )
14411451 {
14421452 int index = this . data . Count ;
1443- GlyphLayoutData glyphWrap = default ;
1453+ GlyphLayoutData glyphData = default ;
14441454 while ( index > 0 )
14451455 {
1446- glyphWrap = this . data [ -- index ] ;
1447- if ( glyphWrap . CodePointIndex == lineBreak . PositionWrap )
1456+ glyphData = this . data [ -- index ] ;
1457+ if ( glyphData . CodePointIndex == lineBreak . PositionWrap )
14481458 {
14491459 break ;
14501460 }
@@ -1455,14 +1465,14 @@ public bool TrySplitAt(LineBreak lineBreak, bool keepAll, [NotNullWhen(true)] ou
14551465 if ( index > 0
14561466 && ! lineBreak . Required
14571467 && keepAll
1458- && UnicodeUtility . IsCJKCodePoint ( ( uint ) glyphWrap . CodePoint . Value ) )
1468+ && UnicodeUtility . IsCJKCodePoint ( ( uint ) glyphData . CodePoint . Value ) )
14591469 {
14601470 // Loop through previous glyphs to see if there is
14611471 // a non CJK codepoint we can break at.
14621472 while ( index > 0 )
14631473 {
1464- glyphWrap = this . data [ -- index ] ;
1465- if ( ! UnicodeUtility . IsCJKCodePoint ( ( uint ) glyphWrap . CodePoint . Value ) )
1474+ glyphData = this . data [ -- index ] ;
1475+ if ( ! UnicodeUtility . IsCJKCodePoint ( ( uint ) glyphData . CodePoint . Value ) )
14661476 {
14671477 index ++ ;
14681478 break ;
@@ -1694,6 +1704,7 @@ private static void RecalculateLineMetrics(TextLine textLine)
16941704 textLine . ScaledMaxAscender = ascender ;
16951705 textLine . ScaledMaxDescender = descender ;
16961706 textLine . ScaledMaxLineHeight = lineHeight ;
1707+ textLine . advances . Clear ( ) ;
16971708 }
16981709
16991710 /// <summary>
0 commit comments