@@ -15,6 +15,7 @@ internal sealed class TextLayout
1515 private readonly float _rubyFontSizeMultiplier ;
1616 private Vector2 _caret = Vector2 . Zero ;
1717 private RectangleF _boundingBox ;
18+ private Line _lastLine ;
1819
1920 private readonly List < Line > _lines = new ( ) ;
2021 private readonly List < GlyphRun > _glyphRuns = new ( ) ;
@@ -70,6 +71,7 @@ public void Clear()
7071 _glyphs . Clear ( ) ;
7172 _lines . Clear ( ) ;
7273 _caret = Vector2 . Zero ;
74+ _lastLine = default ;
7375 _boundingBox = RectangleF . FromLTRB (
7476 float . MaxValue , float . MaxValue ,
7577 float . MinValue , float . MinValue
@@ -81,8 +83,8 @@ public void Append(GlyphRasterizer glyphRasterizer, TextRun textRun)
8183
8284 public void Append ( GlyphRasterizer glyphRasterizer , ReadOnlySpan < TextRun > textRuns )
8385 {
84- bool updateLastLine = _glyphRuns . Count > 0 ;
8586 int appendStart = _glyphs . Count ;
87+ bool updateLastLine = appendStart > 0 ;
8688 var glyphBuf = new List < TextRunGlyph > ( ) ;
8789 var context = new TextLayoutContext
8890 {
@@ -102,17 +104,18 @@ public void Append(GlyphRasterizer glyphRasterizer, ReadOnlySpan<TextRun> textRu
102104 float right = _boundingBox . Right ;
103105 float top = _boundingBox . Top ;
104106 float bottom = _boundingBox . Bottom ;
105- Line prevLine = new ( ) ;
106107 foreach ( Line line in lines )
107108 {
108109 float height = _fixedLineHeight ?? line . VerticalMetrics . LineHeight ;
109- float newY = _caret . Y ;
110- if ( ! updateLastLine && prevLine . VerticalMetrics . LineHeight > 0 )
110+ Vector2 newCaret = _caret ;
111+ if ( _lines . Count > 0 && _lastLine . IsEmpty
112+ || ( ! _lastLine . IsEmpty && ! updateLastLine ) )
111113 {
112- newY += _fixedLineHeight ?? prevLine . VerticalMetrics . LineHeight ;
114+ newCaret . X = 0 ;
115+ newCaret . Y += _fixedLineHeight ?? _lastLine . VerticalMetrics . LineHeight ;
113116 }
114- if ( newY + height > MaxBounds . Height ) { return ; }
115- _caret . Y = newY ;
117+ if ( newCaret . Y + height > MaxBounds . Height ) { return ; }
118+ _caret = newCaret ;
116119
117120 Span < TextRunGlyph > glyphs = CollectionsMarshal . AsSpan ( glyphBuf ) ;
118121 foreach ( TextRunGlyph glyph in glyphs [ line . GlyphSpan ] )
@@ -174,7 +177,7 @@ public void Append(GlyphRasterizer glyphRasterizer, ReadOnlySpan<TextRun> textRu
174177
175178 if ( endGlyphRun )
176179 {
177- AppendGlyphRun ( lastRun . Value , appendStart ) ;
180+ AddGlyphRun ( lastRun . Value , appendStart ) ;
178181 runStart = pos ;
179182 runLength = 1 ;
180183 lastRun = null ;
@@ -192,24 +195,25 @@ public void Append(GlyphRasterizer glyphRasterizer, ReadOnlySpan<TextRun> textRu
192195 line . GlyphSpan . End . Value + appendStart
193196 ) ;
194197
195- if ( updateLastLine )
198+ if ( updateLastLine && ! line . IsEmpty )
196199 {
197200 Line lastLine = _lines [ ^ 1 ] ;
198201 var newSpan = new Range ( lastLine . GlyphSpan . Start , actualLineSpan . End ) ;
199202 _lines [ ^ 1 ] = new Line
200203 {
201204 GlyphSpan = newSpan ,
202- BbLeft = lastLine . BbLeft ,
205+ BbLeft = Math . Min ( line . BbLeft , lastLine . BbLeft ) ,
203206 BbRight = line . BbRight ,
207+ Right = line . Right ,
204208 BaselineY = line . BaselineY ,
205209 VerticalMetrics = line . VerticalMetrics ,
206210 ActualAscender = line . ActualAscender ,
207211 ActualDescender = line . ActualDescender
208212 } ;
209213 }
210- else
214+ else if ( ! updateLastLine )
211215 {
212- _lines . Add ( line . WithSpan ( actualLineSpan ) ) ;
216+ AddLine ( line . WithSpan ( actualLineSpan ) ) ;
213217 }
214218
215219 if ( ! line . IsEmpty )
@@ -219,23 +223,40 @@ public void Append(GlyphRasterizer glyphRasterizer, ReadOnlySpan<TextRun> textRu
219223 right = MathF . Max ( right , line . BbRight ) ;
220224 top = MathF . Min ( top , _caret . Y + line . BaselineY - line . ActualAscender ) ;
221225 }
222- else
223- {
224- _caret . X = 0 ;
225- }
226226 updateLastLine = false ;
227- prevLine = line ;
227+ _lastLine = line ;
228228 }
229229
230230 if ( lastRun . HasValue )
231231 {
232- AppendGlyphRun ( lastRun . Value , appendStart ) ;
232+ AddGlyphRun ( lastRun . Value , appendStart ) ;
233+ }
234+
235+ if ( _lastLine . IsEmpty && ! _lines [ ^ 1 ] . IsEmpty )
236+ {
237+ NewLine ( ) ;
233238 }
234239
235240 _boundingBox = RectangleF . FromLTRB ( left , top , right , bottom ) ;
236241 }
237242
238- private void AppendGlyphRun ( in GlyphRun run , int appendStart )
243+ public void NewLine ( )
244+ {
245+ if ( _lines . Count > 0 )
246+ {
247+ VerticalMetrics vMetrics = _lines [ ^ 1 ] . VerticalMetrics ;
248+ AddLine ( new Line
249+ {
250+ GlyphSpan = new Range ( _glyphs . Count , _glyphs . Count ) ,
251+ BbLeft = float . PositiveInfinity ,
252+ BbRight = float . NegativeInfinity ,
253+ Right = float . NegativeInfinity ,
254+ VerticalMetrics = vMetrics
255+ } ) ;
256+ }
257+ }
258+
259+ private void AddGlyphRun ( in GlyphRun run , int appendStart )
239260 {
240261 var actualSpan = new Range (
241262 run . GlyphSpan . Start . Value + appendStart ,
@@ -248,21 +269,13 @@ private void AppendGlyphRun(in GlyphRun run, int appendStart)
248269 _glyphRuns . Add ( run . WithSpan ( actualSpan ) ) ;
249270 }
250271
251- public void NewLine ( )
272+ private void AddLine ( in Line line )
252273 {
253- if ( _lines . Count > 0 )
274+ _lines . Add ( line ) ;
275+ _lastLine = line ;
276+ if ( line . IsEmpty )
254277 {
255- float newY = _caret . Y + ( _fixedLineHeight ?? _lines [ ^ 1 ] . VerticalMetrics . LineHeight ) ;
256- if ( newY < MaxBounds . Height )
257- {
258- _caret = new Vector2 ( 0 , newY ) ;
259- }
260-
261- _lines . Add ( new Line
262- {
263- GlyphSpan = new Range ( _glyphs . Count , _glyphs . Count ) ,
264- BbLeft = float . MaxValue
265- } ) ;
278+ _caret . X = 0 ;
266279 }
267280 }
268281 }
@@ -472,15 +485,15 @@ public bool MoveNext()
472485 Vector2 caret = _firstLine ? new Vector2 ( _caretStartX , 0 ) : Vector2 . Zero ;
473486 int start = int . MaxValue , end = int . MinValue ;
474487 float ascender = 0 , descender = 0 ;
475- float bbLeft = float . MaxValue ;
488+ float bbLeft = float . PositiveInfinity ;
476489 float bbRight = 0 ;
477490 VerticalMetrics maxVMetrics = new ( ) ;
478491 bool notEmpty = false ;
479492 while ( _peekedWord is { } || _words . MoveNext ( ) )
480493 {
481494 Word word = _peekedWord ?? _words . Current ;
482- bool firstWord = ! notEmpty ;
483495 _peekedWord = null ;
496+ bool firstWord = ! notEmpty ;
484497 float right = caret . X + word . AdvanceWidthNoTrail ;
485498 if ( right > _context . MaxBounds . Width )
486499 {
@@ -510,7 +523,7 @@ public bool MoveNext()
510523 end = Math . Max ( end , word . GlyphRange . End . Value ) ;
511524 ascender = MathF . Max ( ascender , word . ActualAscender ) ;
512525 descender = MathF . Max ( descender , word . ActualDescender ) ;
513- if ( firstWord )
526+ if ( float . IsPositiveInfinity ( bbLeft ) )
514527 {
515528 bbLeft = word . BbLeft ;
516529 }
@@ -568,7 +581,7 @@ public WordEnumerator(in TextLayoutContext context, ReadOnlySpan<TextRun> textRu
568581
569582 public bool MoveNext ( )
570583 {
571- float bbLeft = float . NaN , bbRight = 0 ;
584+ float bbLeft = float . PositiveInfinity , bbRight = 0 ;
572585 float caret = 0 ;
573586 float caretNoTrail = 0 ;
574587 bool hardBreak = false ;
@@ -600,7 +613,7 @@ public bool MoveNext()
600613 ascender = MathF . Max ( ascender , dims . Top ) ;
601614 descender = MathF . Max ( descender , dims . Height - dims . Top - 1 ) ;
602615 bbRight = glyph . Position . X + dims . Width ;
603- if ( float . IsNaN ( bbLeft ) )
616+ if ( float . IsPositiveInfinity ( bbLeft ) )
604617 {
605618 bbLeft = dims . Left ;
606619 }
0 commit comments