@@ -1182,42 +1182,40 @@ VerticalOrientationType.Rotate or
1182
1182
lineBreaks . Add ( lineBreakEnumerator . Current ) ;
1183
1183
}
1184
1184
1185
- int usedOffset = 0 ;
1185
+ int processed = 0 ;
1186
1186
while ( textLine . Count > 0 )
1187
1187
{
1188
1188
LineBreak ? bestBreak = null ;
1189
1189
foreach ( LineBreak lineBreak in lineBreaks )
1190
1190
{
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 )
1196
1193
{
1197
1194
continue ;
1198
1195
}
1199
1196
1200
1197
// 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 )
1203
1200
{
1204
- // Stop and use the best break so far
1205
1201
bestBreak ??= lineBreak ;
1206
1202
break ;
1207
1203
}
1208
1204
1209
- // Update the best break
1210
- bestBreak = lineBreak ;
1211
-
1212
1205
// If it's a mandatory break, stop immediately
1213
1206
if ( lineBreak . Required )
1214
1207
{
1208
+ bestBreak = lineBreak ;
1215
1209
break ;
1216
1210
}
1211
+
1212
+ // Update the best break
1213
+ bestBreak = lineBreak ;
1217
1214
}
1218
1215
1219
1216
if ( bestBreak != null )
1220
1217
{
1218
+ LineBreak breakAt = bestBreak . Value ;
1221
1219
if ( breakAll )
1222
1220
{
1223
1221
// Break-all works differently to the other modes.
@@ -1226,30 +1224,34 @@ VerticalOrientationType.Rotate or
1226
1224
TextLine ? remaining ;
1227
1225
if ( bestBreak . Value . Required )
1228
1226
{
1229
- if ( textLine . TrySplitAt ( bestBreak . Value , keepAll , out remaining ) )
1227
+ if ( textLine . TrySplitAt ( breakAt , keepAll , out remaining ) )
1230
1228
{
1231
- usedOffset += textLine . Count ;
1229
+ processed = breakAt . PositionWrap ;
1232
1230
textLines . Add ( textLine . Finalize ( options ) ) ;
1233
1231
textLine = remaining ;
1234
1232
}
1235
1233
}
1236
1234
else if ( textLine . TrySplitAt ( wrappingLength , out remaining ) )
1237
1235
{
1238
- usedOffset += textLine . Count ;
1236
+ processed += textLine . Count ;
1239
1237
textLines . Add ( textLine . Finalize ( options ) ) ;
1240
1238
textLine = remaining ;
1241
1239
}
1242
1240
else
1243
1241
{
1244
- usedOffset += textLine . Count ;
1242
+ processed += textLine . Count ;
1245
1243
}
1246
1244
}
1247
1245
else
1248
1246
{
1249
1247
// 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 ) )
1251
1249
{
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
+
1253
1255
if ( breakWord )
1254
1256
{
1255
1257
// A break was found, but we need to check if the line is too long
@@ -1258,7 +1260,7 @@ VerticalOrientationType.Rotate or
1258
1260
textLine . TrySplitAt ( wrappingLength , out TextLine ? overflow ) )
1259
1261
{
1260
1262
// Reinsert the overflow at the beginning of the remaining line
1261
- usedOffset -= overflow . Count ;
1263
+ processed -= overflow . Count ;
1262
1264
remaining . InsertAt ( 0 , overflow ) ;
1263
1265
}
1264
1266
}
@@ -1269,13 +1271,14 @@ VerticalOrientationType.Rotate or
1269
1271
}
1270
1272
else
1271
1273
{
1272
- usedOffset += textLine . Count ;
1274
+ processed += textLine . Count ;
1273
1275
}
1274
1276
}
1275
1277
}
1276
1278
else
1277
1279
{
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.
1279
1282
if ( breakWord || breakAll )
1280
1283
{
1281
1284
while ( textLine . ScaledLineAdvance > wrappingLength )
@@ -1316,13 +1319,16 @@ public float ScaledMaxAdvance()
1316
1319
internal sealed class TextLine
1317
1320
{
1318
1321
private readonly List < GlyphLayoutData > data ;
1322
+ private readonly Dictionary < int , float > advances = new ( ) ;
1319
1323
1320
1324
public TextLine ( ) => this . data = new ( 16 ) ;
1321
1325
1322
1326
public TextLine ( int capacity ) => this . data = new ( capacity ) ;
1323
1327
1324
1328
public int Count => this . data . Count ;
1325
1329
1330
+ public int LeadCodePointIndex { get ; private set ; } = - 1 ;
1331
+
1326
1332
public float ScaledLineAdvance { get ; private set ; }
1327
1333
1328
1334
public float ScaledMaxLineHeight { get ; private set ; } = - 1 ;
@@ -1355,6 +1361,11 @@ public void Add(
1355
1361
this . ScaledLineAdvance += scaledAdvance ;
1356
1362
}
1357
1363
1364
+ if ( this . LeadCodePointIndex == - 1 )
1365
+ {
1366
+ this . LeadCodePointIndex = codePointIndex ;
1367
+ }
1368
+
1358
1369
this . ScaledMaxLineHeight = MathF . Max ( this . ScaledMaxLineHeight , scaledLineHeight ) ;
1359
1370
this . ScaledMaxAscender = MathF . Max ( this . ScaledMaxAscender , scaledAscender ) ;
1360
1371
this . ScaledMaxDescender = MathF . Max ( this . ScaledMaxDescender , scaledDescender ) ;
@@ -1383,6 +1394,11 @@ public void InsertAt(int index, TextLine textLine)
1383
1394
1384
1395
public float MeasureAt ( int index )
1385
1396
{
1397
+ if ( this . advances . TryGetValue ( index , out float advance ) )
1398
+ {
1399
+ return advance ;
1400
+ }
1401
+
1386
1402
if ( index >= this . data . Count )
1387
1403
{
1388
1404
index = this . data . Count - 1 ;
@@ -1395,12 +1411,13 @@ public float MeasureAt(int index)
1395
1411
index -- ;
1396
1412
}
1397
1413
1398
- float advance = 0 ;
1414
+ advance = 0 ;
1399
1415
for ( int i = 0 ; i <= index ; i ++ )
1400
1416
{
1401
1417
advance += this . data [ i ] . ScaledAdvance ;
1402
1418
}
1403
1419
1420
+ this . advances [ index ] = advance ;
1404
1421
return advance ;
1405
1422
}
1406
1423
@@ -1440,11 +1457,11 @@ public bool TrySplitAt(float length, [NotNullWhen(true)] out TextLine? result)
1440
1457
public bool TrySplitAt ( LineBreak lineBreak , bool keepAll , [ NotNullWhen ( true ) ] out TextLine ? result )
1441
1458
{
1442
1459
int index = this . data . Count ;
1443
- GlyphLayoutData glyphWrap = default ;
1460
+ GlyphLayoutData glyphData = default ;
1444
1461
while ( index > 0 )
1445
1462
{
1446
- glyphWrap = this . data [ -- index ] ;
1447
- if ( glyphWrap . CodePointIndex == lineBreak . PositionWrap )
1463
+ glyphData = this . data [ -- index ] ;
1464
+ if ( glyphData . CodePointIndex == lineBreak . PositionWrap )
1448
1465
{
1449
1466
break ;
1450
1467
}
@@ -1455,14 +1472,14 @@ public bool TrySplitAt(LineBreak lineBreak, bool keepAll, [NotNullWhen(true)] ou
1455
1472
if ( index > 0
1456
1473
&& ! lineBreak . Required
1457
1474
&& keepAll
1458
- && UnicodeUtility . IsCJKCodePoint ( ( uint ) glyphWrap . CodePoint . Value ) )
1475
+ && UnicodeUtility . IsCJKCodePoint ( ( uint ) glyphData . CodePoint . Value ) )
1459
1476
{
1460
1477
// Loop through previous glyphs to see if there is
1461
1478
// a non CJK codepoint we can break at.
1462
1479
while ( index > 0 )
1463
1480
{
1464
- glyphWrap = this . data [ -- index ] ;
1465
- if ( ! UnicodeUtility . IsCJKCodePoint ( ( uint ) glyphWrap . CodePoint . Value ) )
1481
+ glyphData = this . data [ -- index ] ;
1482
+ if ( ! UnicodeUtility . IsCJKCodePoint ( ( uint ) glyphData . CodePoint . Value ) )
1466
1483
{
1467
1484
index ++ ;
1468
1485
break ;
@@ -1694,6 +1711,8 @@ private static void RecalculateLineMetrics(TextLine textLine)
1694
1711
textLine . ScaledMaxAscender = ascender ;
1695
1712
textLine . ScaledMaxDescender = descender ;
1696
1713
textLine . ScaledMaxLineHeight = lineHeight ;
1714
+ textLine . LeadCodePointIndex = textLine [ 0 ] . CodePointIndex ;
1715
+ textLine . advances . Clear ( ) ;
1697
1716
}
1698
1717
1699
1718
/// <summary>
0 commit comments