/// <summary> /// Paint this line /// </summary> /// <param name="ctx">The paint context</param> internal void Paint(PaintTextContext ctx) { foreach (var r in Runs) { r.Paint(ctx); } }
/// <summary> /// Paint background of this font run /// </summary> /// <param name="ctx"></param> internal void PaintBackground(PaintTextContext ctx) { if (Style.BackgroundColor != SKColor.Empty && RunKind == FontRunKind.Normal) { var rect = new SKRect(XCoord, Line.YCoord, XCoord + Width, Line.YCoord + Line.Height); using (var skPaint = new SKPaint { Style = SKPaintStyle.Fill, Color = Style.BackgroundColor }) { ctx.Canvas.DrawRect(rect, skPaint); } } }
/// <summary> /// Paint this font run /// </summary> /// <param name="ctx"></param> internal void Paint(PaintTextContext ctx) { // Paint selection? if (ctx.PaintSelectionBackground != null && RunKind != FontRunKind.Ellipsis) { float selStartXCoord; if (ctx.SelectionStart < Start) { selStartXCoord = Direction == TextDirection.LTR ? 0 : Width; } else if (ctx.SelectionStart >= End) { selStartXCoord = Direction == TextDirection.LTR ? Width : 0; } else { selStartXCoord = RelativeCodePointXCoords[ctx.SelectionStart - this.Start]; } float selEndXCoord; if (ctx.SelectionEnd < Start) { selEndXCoord = Direction == TextDirection.LTR ? 0 : Width; } else if (ctx.SelectionEnd >= End) { selEndXCoord = Direction == TextDirection.LTR ? Width : 0; } else { selEndXCoord = RelativeCodePointXCoords[ctx.SelectionEnd - this.Start]; } if (selStartXCoord != selEndXCoord) { var tl = new SKPoint(selStartXCoord + this.XCoord, Line.YCoord); var br = new SKPoint(selEndXCoord + this.XCoord, Line.YCoord + Line.Height); // Align coords to pixel boundaries // Not needed - disabled antialias on SKPaint instead /* * if (ctx.Canvas.TotalMatrix.TryInvert(out var inverse)) * { * tl = ctx.Canvas.TotalMatrix.MapPoint(tl); * br = ctx.Canvas.TotalMatrix.MapPoint(br); * tl = new SKPoint((float)Math.Round(tl.X), (float)Math.Round(tl.Y)); * br = new SKPoint((float)Math.Round(br.X), (float)Math.Round(br.Y)); * tl = inverse.MapPoint(tl); * br = inverse.MapPoint(br); * } */ var rect = new SKRect(tl.X, tl.Y, br.X, br.Y); ctx.Canvas.DrawRect(rect, ctx.PaintSelectionBackground); } } // Don't paint trailing whitespace runs if (RunKind == FontRunKind.TrailingWhitespace) { return; } // Text using (var paint = new SKPaint()) { // Work out font variant adjustments float glyphScale = 1; float glyphVOffset = 0; if (Style.FontVariant == FontVariant.SuperScript) { glyphScale = 0.65f; glyphVOffset = -Style.FontSize * 0.35f; } if (Style.FontVariant == FontVariant.SubScript) { glyphScale = 0.65f; glyphVOffset = Style.FontSize * 0.1f; } // Setup SKPaint paint.Color = Style.TextColor; paint.IsAntialias = ctx.Options.IsAntialias; paint.LcdRenderText = ctx.Options.LcdRenderText; unsafe { fixed(ushort *pGlyphs = Glyphs.Underlying) { // Get glyph positions var glyphPositions = GlyphPositions.ToArray(); // Create the font if (_font == null) { _font = new SKFont(this.Typeface, this.Style.FontSize * glyphScale); _font.Subpixel = true; } // Create the SKTextBlob (if necessary) if (_textBlob == null) { _textBlob = SKTextBlob.CreatePositioned( (IntPtr)(pGlyphs + Glyphs.Start), Glyphs.Length * sizeof(ushort), SKTextEncoding.GlyphId, _font, GlyphPositions.AsSpan()); } // Paint underline if (Style.Underline != UnderlineStyle.None && RunKind == FontRunKind.Normal) { // Work out underline metrics float underlineYPos = Line.YCoord + Line.BaseLine + (_font.Metrics.UnderlinePosition ?? 0); paint.StrokeWidth = _font.Metrics.UnderlineThickness ?? 1; if (Style.Underline == UnderlineStyle.Gapped) { // Get intercept positions var interceptPositions = _textBlob.GetIntercepts(underlineYPos - paint.StrokeWidth / 2, underlineYPos + paint.StrokeWidth); // Paint gapped underlinline float x = XCoord; for (int i = 0; i < interceptPositions.Length; i += 2) { float b = interceptPositions[i] - paint.StrokeWidth; if (x < b) { ctx.Canvas.DrawLine(new SKPoint(x, underlineYPos), new SKPoint(b, underlineYPos), paint); } x = interceptPositions[i + 1] + paint.StrokeWidth; } if (x < XCoord + Width) { ctx.Canvas.DrawLine(new SKPoint(x, underlineYPos), new SKPoint(XCoord + Width, underlineYPos), paint); } } else { switch (Style.Underline) { case UnderlineStyle.ImeInput: paint.PathEffect = SKPathEffect.CreateDash(new float[] { paint.StrokeWidth, paint.StrokeWidth }, paint.StrokeWidth); break; case UnderlineStyle.ImeConverted: paint.PathEffect = SKPathEffect.CreateDash(new float[] { paint.StrokeWidth, paint.StrokeWidth }, paint.StrokeWidth); break; case UnderlineStyle.ImeTargetConverted: paint.StrokeWidth *= 2; break; case UnderlineStyle.ImeTargetNonConverted: break; } // Paint solid underline ctx.Canvas.DrawLine(new SKPoint(XCoord, underlineYPos), new SKPoint(XCoord + Width, underlineYPos), paint); paint.PathEffect = null; } } ctx.Canvas.DrawText(_textBlob, 0, 0, paint); } } // Paint strikethrough if (Style.StrikeThrough != StrikeThroughStyle.None && RunKind == FontRunKind.Normal) { paint.StrokeWidth = _font.Metrics.StrikeoutThickness ?? 0; float strikeYPos = Line.YCoord + Line.BaseLine + (_font.Metrics.StrikeoutPosition ?? 0) + glyphVOffset; ctx.Canvas.DrawLine(new SKPoint(XCoord, strikeYPos), new SKPoint(XCoord + Width, strikeYPos), paint); } } }
/// <summary> /// Paint this font run /// </summary> /// <param name="ctx"></param> internal void Paint(PaintTextContext ctx) { // Paint selection? if (ctx.PaintSelectionBackground != null) { float selStartXCoord; if (ctx.SelectionStart < Start) { selStartXCoord = Direction == TextDirection.LTR ? 0 : Width; } else if (ctx.SelectionStart >= End) { selStartXCoord = Direction == TextDirection.LTR ? Width : 0; } else { selStartXCoord = RelativeCodePointXCoords[ctx.SelectionStart - this.Start]; } float selEndXCoord; if (ctx.SelectionEnd < Start) { selEndXCoord = Direction == TextDirection.LTR ? 0 : Width; } else if (ctx.SelectionEnd >= End) { selEndXCoord = Direction == TextDirection.LTR ? Width : 0; } else { selEndXCoord = RelativeCodePointXCoords[ctx.SelectionEnd - this.Start]; } if (selStartXCoord != selEndXCoord) { var rect = new SKRect(this.XCoord + selStartXCoord, Line.YCoord, this.XCoord + selEndXCoord, Line.YCoord + Line.Height); ctx.Canvas.DrawRect(rect, ctx.PaintSelectionBackground); } } // Don't paint trailing whitespace runs if (RunKind == FontRunKind.TrailingWhitespace) { return; } // Text using (var paint = new SKPaint()) { // Work out font variant adjustments float glyphScale = 1; float glyphVOffset = 0; if (Style.FontVariant == FontVariant.SuperScript) { glyphScale = 0.65f; glyphVOffset = -Style.FontSize * 0.35f; } if (Style.FontVariant == FontVariant.SubScript) { glyphScale = 0.65f; glyphVOffset = Style.FontSize * 0.1f; } // Setup SKPaint paint.Color = Style.TextColor; paint.TextEncoding = SKTextEncoding.GlyphId; paint.Typeface = Typeface; paint.TextSize = Style.FontSize * glyphScale; paint.SubpixelText = true; paint.IsAntialias = ctx.Options.IsAntialias; paint.LcdRenderText = ctx.Options.LcdRenderText; unsafe { fixed(ushort *pGlyphs = Glyphs.Underlying) { // Get glyph positions var glyphPositions = GlyphPositions.ToArray(); // Paint underline if (Style.Underline != UnderlineStyle.None && RunKind == FontRunKind.Normal) { // Work out underline metrics paint.TextSize = Style.FontSize; float underlineYPos = Line.YCoord + Line.BaseLine + (paint.FontMetrics.UnderlinePosition ?? 0); paint.StrokeWidth = paint.FontMetrics.UnderlineThickness ?? 0; paint.TextSize = Style.FontSize * glyphScale; if (Style.Underline == UnderlineStyle.Gapped) { // Get intercept positions var interceptPositions = paint.GetPositionedTextIntercepts( (IntPtr)(pGlyphs + Glyphs.Start), Glyphs.Length * sizeof(ushort), glyphPositions, underlineYPos - paint.StrokeWidth / 2, underlineYPos + paint.StrokeWidth); // Paint gapped underlinline float x = XCoord; for (int i = 0; i < interceptPositions.Length; i += 2) { float b = interceptPositions[i] - paint.StrokeWidth; if (x < b) { ctx.Canvas.DrawLine(new SKPoint(x, underlineYPos), new SKPoint(b, underlineYPos), paint); } x = interceptPositions[i + 1] + paint.StrokeWidth; } if (x < XCoord + Width) { ctx.Canvas.DrawLine(new SKPoint(x, underlineYPos), new SKPoint(XCoord + Width, underlineYPos), paint); } } else { // Paint solid underline ctx.Canvas.DrawLine(new SKPoint(XCoord, underlineYPos), new SKPoint(XCoord + Width, underlineYPos), paint); } } // Draw the text ctx.Canvas.DrawPositionedText((IntPtr)(pGlyphs + Glyphs.Start), Glyphs.Length * sizeof(ushort), glyphPositions, paint); } } // Paint strikethrough if (Style.StrikeThrough != StrikeThroughStyle.None && RunKind == FontRunKind.Normal) { paint.StrokeWidth = paint.FontMetrics.StrikeoutThickness ?? 0; float strikeYPos = Line.YCoord + Line.BaseLine + (paint.FontMetrics.StrikeoutPosition ?? 0) + glyphVOffset; ctx.Canvas.DrawLine(new SKPoint(XCoord, strikeYPos), new SKPoint(XCoord + Width, strikeYPos), paint); } } }