Skip to content

Commit b52f8f8

Browse files
Fix decoration continuation
1 parent e8f39c7 commit b52f8f8

File tree

2 files changed

+67
-11
lines changed

2 files changed

+67
-11
lines changed

src/ImageSharp.Drawing/Processing/Processors/Text/RichTextGlyphRenderer.cs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -613,15 +613,4 @@ public readonly void Dispose()
613613
this.OutlineMap?.Dispose();
614614
}
615615
}
616-
617-
private struct TextDecorationDetails
618-
{
619-
public Vector2 Start { get; set; }
620-
621-
public Vector2 End { get; set; }
622-
623-
public Pen Pen { get; set; }
624-
625-
public float Thickness { get; internal set; }
626-
}
627616
}

src/ImageSharp.Drawing/Shapes/Text/BaseGlyphBuilder.cs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ internal class BaseGlyphBuilder : IGlyphRenderer
2828
private int graphemePathCount;
2929
private int currentGraphemeIndex = -1;
3030
private readonly List<GlyphPathCollection> currentGlyphs = [];
31+
private TextDecorationDetails? previousUnderlineTextDecoration;
32+
private TextDecorationDetails? previousOverlineTextDecoration;
33+
private TextDecorationDetails? previousStrikeoutTextDecoration;
3134

3235
// Per-layer (within current grapheme) bookkeeping:
3336
private int layerStartIndex;
@@ -67,6 +70,9 @@ void IGlyphRenderer.EndText()
6770
this.graphemeBuilder = null;
6871
this.graphemePathCount = 0;
6972
this.currentGraphemeIndex = -1;
73+
this.previousUnderlineTextDecoration = null;
74+
this.previousOverlineTextDecoration = null;
75+
this.previousStrikeoutTextDecoration = null;
7076

7177
this.EndText();
7278
}
@@ -248,6 +254,56 @@ void IGlyphRenderer.SetDecoration(TextDecorations textDecorations, Vector2 start
248254
start = ClampToPixel(start, (int)thickness, rotated);
249255
end = ClampToPixel(end, (int)thickness, rotated);
250256

257+
// Sometimes the start and end points do not align properly leaving pixel sized gaps
258+
// so we need to adjust them. Use any previous decoration to try and continue the line.
259+
TextDecorationDetails? previous = textDecorations switch
260+
{
261+
TextDecorations.Underline => this.previousUnderlineTextDecoration,
262+
TextDecorations.Overline => this.previousOverlineTextDecoration,
263+
TextDecorations.Strikeout => this.previousStrikeoutTextDecoration,
264+
_ => null
265+
};
266+
267+
if (previous != null)
268+
{
269+
// Align the new line with the previous one if they are close enough.
270+
if (rotated)
271+
{
272+
if (thickness == previous.Value.Thickness
273+
&& previous.Value.End.Y + 1 >= start.Y
274+
&& previous.Value.End.X == start.X)
275+
{
276+
start = previous.Value.End;
277+
}
278+
}
279+
else if (thickness == previous.Value.Thickness
280+
&& previous.Value.End.Y == start.Y
281+
&& previous.Value.End.X + 1 >= start.X)
282+
{
283+
start = previous.Value.End;
284+
}
285+
}
286+
287+
TextDecorationDetails current = new()
288+
{
289+
Start = start,
290+
End = end,
291+
Thickness = thickness
292+
};
293+
294+
switch (textDecorations)
295+
{
296+
case TextDecorations.Underline:
297+
this.previousUnderlineTextDecoration = current;
298+
break;
299+
case TextDecorations.Strikeout:
300+
this.previousStrikeoutTextDecoration = current;
301+
break;
302+
case TextDecorations.Overline:
303+
this.previousOverlineTextDecoration = current;
304+
break;
305+
}
306+
251307
Vector2 a = start - pad;
252308
Vector2 b = start + pad;
253309
Vector2 c = end + pad;
@@ -343,16 +399,27 @@ public virtual void SetDecoration(TextDecorations textDecorations, Vector2 start
343399
[MethodImpl(MethodImplOptions.AggressiveInlining)]
344400
private static PointF ClampToPixel(PointF point, int thickness, bool rotated)
345401
{
402+
// Even. Clamp to whole pixels.
346403
if ((thickness & 1) == 0)
347404
{
348405
return Point.Truncate(point);
349406
}
350407

408+
// Odd. Clamp to half pixels.
351409
if (rotated)
352410
{
353411
return Point.Truncate(point) + new Vector2(.5F, 0);
354412
}
355413

356414
return Point.Truncate(point) + new Vector2(0, .5F);
357415
}
416+
417+
private struct TextDecorationDetails
418+
{
419+
public Vector2 Start { get; set; }
420+
421+
public Vector2 End { get; set; }
422+
423+
public float Thickness { get; internal set; }
424+
}
358425
}

0 commit comments

Comments
 (0)