private void DrawHorizontalRuler(SKCanvas canvas, float canvasWidth, int RulerWidth, int LargeSteps, int SmallSteps) { using (var paint = new SKPaint { Color = SKColors.Black, StrokeWidth = 1 * _displayScale, TextAlign = SKTextAlign.Center, }) { canvas.DrawLine(0, 0, canvasWidth, 0, paint); canvas.DrawLine(0, RulerWidth, canvasWidth, RulerWidth, paint); for (int x = 0; x < canvasWidth; x += LargeSteps) { for (int x1 = x + SmallSteps; x1 < x + LargeSteps; x1 += SmallSteps) { canvas.DrawLine(x1, 0, x1, 10, paint); } canvas.DrawLine(x, 0, x, 20, paint); var typeface = SKTypeface.FromFamilyName(FontFamily.Source); var font = new SKFont(typeface, (float)FontSize * _displayScale); var measurementText = SKTextBlob.Create($"{x}", font); canvas.DrawText(measurementText, x, 30, paint); } } }
private static void DrawGrid(SKCanvas canvas, float canvasWidth, float canvasHeight, SKPaint paint) { paint.PathEffect = SKPathEffect.CreateDash(new float[] { 20, 20 }, 0); for (int i = 0; i <= 8; i++) { var x = i * canvasWidth / 8; var y = i * canvasHeight / 8; int textOffset; if (i > 0 && i < 8) { canvas.DrawLine(new SKPoint(x, 0), new SKPoint(x, canvasHeight), paint); canvas.DrawLine(new SKPoint(0, y), new SKPoint(canvasWidth, y), paint); textOffset = 0; } else if (i == 0) { textOffset = 15; } else { textOffset = -15; } var font = new SKFont(SKTypeface.Default, 12.0f); var measurementText = SKTextBlob.Create($"{i * 10}", font); canvas.DrawText(measurementText, x + textOffset, 10, paint); canvas.DrawText(measurementText, 5, y + textOffset, paint); } }
public void DrawText(SKTextBlob text, float x, float y, SKPaint paint) { canvas.DrawText(text, x, y, paint); if (calculateBounds) { displayObject.addBoundingRect(text.Bounds); } }
void Reset() { RunKind = FontRunKind.Normal; CodePointBuffer = null; Style = null; Typeface = null; Line = null; _textBlob = null; _font = null; }
public unsafe void TextInterceptsAreFoundCorrectly() { var text = "|"; var font = new SKFont(); font.Size = 100; var blob = SKTextBlob.Create(text, font, new SKPoint(50, 100)); var widths = blob.GetIntercepts(0, 100); Assert.Equal(2, widths.Length); var diff = widths[1] - widths[0]; var textPath = font.GetTextPath(text, SKPoint.Empty); var pathWidth = textPath.TightBounds.Width; Assert.Equal(pathWidth, diff, 2); }
public GlyphRunImpl(SKTextBlob textBlob) { TextBlob = textBlob; }
public GlyphRunImpl(SKPaint paint, SKTextBlob textBlob) { Paint = paint; TextBlob = textBlob; }
/// <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); } } }
public GlyphRunImpl([NotNull] SKTextBlob textBlob) { TextBlob = textBlob ?? throw new ArgumentNullException(nameof(textBlob)); }
public SKRenderable(SKTextBlob text) { skiaObject = text; renderImpl = RenderTextBlob; }
public void Draw(SKCanvas canvas, IReadOnlyViewport viewport, IWidget widget, float layerOpacity) { canvas.RotateDegrees((float)viewport.Rotation, 0.0f, 0.0f); var TL = viewport.ScreenToWorld(canvas.LocalClipBounds.Left, canvas.LocalClipBounds.Top); var BR = viewport.ScreenToWorld(canvas.LocalClipBounds.Right, canvas.LocalClipBounds.Bottom); var width = viewport.Extent.Width; var height = viewport.Extent.Height; var usedLineOffset = lineOffset; uint usedLineOffsetInt = (uint)usedLineOffset; IEnumerable <SKPaint> usedPaints = paints; if (viewport.Resolution > 0.25) { usedLineOffset *= 10; // hide 10m layer usedLineOffsetInt *= 10; // hide 10m layer usedPaints = paintsL2; } if (viewport.Resolution > 3) { usedLineOffset *= 10; // hide 100m layer usedLineOffsetInt *= 10; // hide 100m layer usedPaints = paintsL3; } int lineCount100M = (int)(100 / usedLineOffset); //How many lines are 1000m int lineCount1000M = (int)(1000 / usedLineOffset); if (lineCount100M == 0) { lineCount100M = 9000; //will never be reached, if we only render 1k's } double screenWidthPerLine = canvas.LocalClipBounds.Width / (width / usedLineOffset); double screenHeightPerLine = canvas.LocalClipBounds.Height / (height / usedLineOffset); //World coordinates of first lineOffset line var first100mW = (TL.X + (usedLineOffset - TL.X % usedLineOffset)); var first100mH = (TL.Y + (usedLineOffset - TL.Y % usedLineOffset)); //Screen offset of first lineOffset line double offsetCW = ((first100mW - TL.X) / usedLineOffset) * screenWidthPerLine; double offsetCH = screenHeightPerLine + ((TL.Y - first100mH) / usedLineOffset) * screenHeightPerLine; //offset of next 1k int KOffsetW = (int)((first100mW % 1000) / usedLineOffset); if (KOffsetW < 0) { KOffsetW = 10 + KOffsetW; } int KOffsetH = (int)((first100mH % 1000) / usedLineOffset) - 1; if (lineCount1000M > 1) { KOffsetH = lineCount1000M - KOffsetH; } for (double curX = offsetCW; curX < canvas.LocalClipBounds.Right; curX += screenWidthPerLine) { var worldPos = viewport.ScreenToWorld(curX, 0).X; var Xgrid10KM = (uint)(worldPos / 10000) % 10; var Xgrid1KM = (uint)(worldPos / 1000) % 10; var Xgrid100m = (uint)(worldPos / 100) % 10; //var Xgrid10m = (uint)(worldPos / 10) % 10; string gridPosString; if (usedLineOffsetInt == 1000) { gridPosString = $"{Xgrid10KM}{Xgrid1KM}"; } else if (usedLineOffsetInt == 100) { gridPosString = $"{Xgrid10KM}{Xgrid1KM}{Xgrid100m}"; } else { gridPosString = $"{Xgrid10KM}{Xgrid1KM}{Xgrid100m}{(uint)(worldPos / 10) % 10}"; } SKPaint paint; if (KOffsetW >= 1000 && KOffsetW % 1000 == 0) { paint = usedPaints.ElementAt(0); } if (KOffsetW >= 100 && KOffsetW % 100 == 0) { paint = usedPaints.ElementAt(2); } else if (KOffsetW >= 10 && KOffsetW % 10 == 0) { paint = usedPaints.ElementAt(1); } else { paint = usedPaints.ElementAt(0); } if (KOffsetW == lineCount1000M) { KOffsetW = 0; } canvas.DrawLine((float)curX, 0, (float)curX, canvas.LocalClipBounds.Height, paint); using (var gtext = SKTextBlob.Create(gridPosString, markerFont)) canvas.DrawText(gtext, (float)(curX + screenWidthPerLine / 2) - gtext.Bounds.MidX, 20 + gtext.Bounds.MidY, paint100m); KOffsetW++; } for (double curH = offsetCH; curH < canvas.LocalClipBounds.Bottom; curH += screenHeightPerLine) { var worldPos = viewport.ScreenToWorld(0, curH).Y; var Xgrid10KM = (uint)(worldPos / 10000) % 10; var Xgrid1KM = (uint)(worldPos / 1000) % 10; var Xgrid100m = (uint)(worldPos / 100) % 10; //var Xgrid10m = (uint)(worldPos / 10) % 10; string gridPosString; if (usedLineOffsetInt == 1000) { gridPosString = $"{Xgrid10KM}{Xgrid1KM}"; } else if (usedLineOffsetInt == 100) { gridPosString = $"{Xgrid10KM}{Xgrid1KM}{Xgrid100m}"; } else { gridPosString = $"{Xgrid10KM}{Xgrid1KM}{Xgrid100m}{(uint)(worldPos / 10) % 10}"; } SKPaint paint; if (KOffsetH >= 100 && KOffsetH % 100 == 0) { paint = usedPaints.ElementAt(2); } else if (KOffsetH >= 10 && KOffsetH % 10 == 0) { paint = usedPaints.ElementAt(1); } else { paint = usedPaints.ElementAt(0); } if (KOffsetH == lineCount1000M) { KOffsetH = 0; } canvas.DrawLine(0, (float)curH, canvas.LocalClipBounds.Width, (float)curH, paint); using (var gtext = SKTextBlob.Create(gridPosString, markerFont)) canvas.DrawText(gtext, 0, (float)(curH + screenWidthPerLine / 2) - gtext.Bounds.MidY, paint100m); KOffsetH++; } }