Pfade und Text in SkiaSharp
Erkunden der Schnittmenge von Pfaden und Text
In modernen Grafiksystemen sind Textschriftarten Sammlungen von Zeichengliederungen, die in der Regel durch quadratische Bézierkurven definiert werden. Folglich enthalten viele moderne Grafiksysteme eine Möglichkeit zum Konvertieren von Textzeichen in einen Grafikpfad.
Sie haben bereits gesehen, dass Sie die Gliederungen von Textzeichen strichen und sie ausfüllen können. Auf diese Weise können Sie diese Zeichenkonturen mit einer bestimmten Strichbreite und sogar einem Pfadeffekt anzeigen, wie im Artikel "Pfadeffekte " beschrieben. Es ist aber auch möglich, eine Zeichenfolge in ein SKPath
Objekt zu konvertieren. Dies bedeutet, dass Textkonturen für den Clipping mit Techniken verwendet werden können, die im Artikel "Clipping with Paths and Regions " beschrieben wurden.
Neben der Verwendung eines Pfadeffekts zum Strich einer Zeichengliederung können Sie auch Pfadeffekte erstellen, die auf einem Pfad basieren, der von einer Zeichenfolge abgeleitet wird, und Sie können sogar die beiden Effekte kombinieren:
Im vorherigen Artikel zu Pfadeffekten haben Sie gesehen, wie die GetFillPath
Methode SKPaint
eine Gliederung eines Strichpfads abrufen kann. Sie können diese Methode auch mit Pfaden verwenden, die von Zeichengliederungen abgeleitet sind.
Schließlich veranschaulicht dieser Artikel eine weitere Schnittmenge von Pfaden und Text: Mit der DrawTextOnPath
Methode SKCanvas
können Sie eine Textzeichenfolge anzeigen, sodass der Basisplan des Texts einem gekrümmten Pfad folgt.
Text in Pfadkonvertierung
Die GetTextPath
Methode der SKPaint
Konvertierung einer Zeichenfolge in ein SKPath
Objekt:
public SKPath GetTextPath (String text, Single x, Single y)
Die Argumente und y
die x
Argumente geben den Anfangspunkt der Basislinie der linken Seite des Texts an. Sie spielen hier die gleiche Rolle wie bei der DrawText
Methode der SKCanvas
. Innerhalb des Pfads hat der Basisplan der linken Seite des Texts die Koordinaten (x, y).
Die GetTextPath
Methode ist überlastend, wenn Sie lediglich den resultierenden Pfad ausfüllen oder strichen möchten. Mit der normalen DrawText
Methode können Sie dies tun. Die GetTextPath
Methode ist nützlicher für andere Aufgaben, die Pfade betreffen.
Eine dieser Aufgaben ist das Ausschneiden. Die Seite "Clipping Text" erstellt basierend auf den Zeichengliederungen des Worts "CODE" einen Clippingpfad. Dieser Pfad wird auf die Größe der Seite gestreckt, um eine Bitmap zu beschneiden, die ein Bild des Quellcodes für clipping-Text enthält:
Der ClippingTextPage
Klassenkonstruktor lädt die Bitmap, die als eingebettete Ressource im Ordner "Medien " der Lösung gespeichert ist:
public class ClippingTextPage : ContentPage
{
SKBitmap bitmap;
public ClippingTextPage()
{
Title = "Clipping Text";
SKCanvasView canvasView = new SKCanvasView();
canvasView.PaintSurface += OnCanvasViewPaintSurface;
Content = canvasView;
string resourceID = "SkiaSharpFormsDemos.Media.PageOfCode.png";
Assembly assembly = GetType().GetTypeInfo().Assembly;
using (Stream stream = assembly.GetManifestResourceStream(resourceID))
{
bitmap = SKBitmap.Decode(stream);
}
}
...
}
Der PaintSurface
Handler beginnt mit dem Erstellen eines Objekts, das SKPaint
für Text geeignet ist. Die Typeface
Eigenschaft wird ebenso festgelegt wie die TextSize
, obwohl für diese bestimmte Anwendung die TextSize
Eigenschaft rein willkürlich ist. Beachten Sie außerdem, dass keine Style
Einstellung vorhanden ist.
Die TextSize
Einstellungen und Style
Eigenschaften sind nicht erforderlich, da dieses SKPaint
Objekt ausschließlich für den GetTextPath
Aufruf mit der Textzeichenfolge "CODE" verwendet wird. Der Handler misst dann das resultierende SKPath
Objekt und wendet drei Transformationen an, um es zu zentrieren und auf die Größe der Seite zu skalieren. Der Pfad kann dann als Clippingpfad festgelegt werden:
public class ClippingTextPage : ContentPage
{
...
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear(SKColors.Blue);
using (SKPaint paint = new SKPaint())
{
paint.Typeface = SKTypeface.FromFamilyName(null, SKTypefaceStyle.Bold);
paint.TextSize = 10;
using (SKPath textPath = paint.GetTextPath("CODE", 0, 0))
{
// Set transform to center and enlarge clip path to window height
SKRect bounds;
textPath.GetTightBounds(out bounds);
canvas.Translate(info.Width / 2, info.Height / 2);
canvas.Scale(info.Width / bounds.Width, info.Height / bounds.Height);
canvas.Translate(-bounds.MidX, -bounds.MidY);
// Set the clip path
canvas.ClipPath(textPath);
}
}
// Reset transforms
canvas.ResetMatrix();
// Display bitmap to fill window but maintain aspect ratio
SKRect rect = new SKRect(0, 0, info.Width, info.Height);
canvas.DrawBitmap(bitmap,
rect.AspectFill(new SKSize(bitmap.Width, bitmap.Height)));
}
}
Sobald der Clippingpfad festgelegt ist, kann die Bitmap angezeigt werden, und sie wird auf die Zeichengliederungen zugeschnitten. Beachten Sie die Verwendung der Methode, mit SKRect
der AspectFill
ein Rechteck für das Ausfüllen der Seite berechnet wird, während das Seitenverhältnis beibehalten wird.
Auf der Seite "Textpfadeffekt " wird ein einzelnes kaufmännisches Und-Zeichen in einen Pfad konvertiert, um einen 1D-Pfadeffekt zu erstellen. Ein Paint-Objekt mit diesem Pfadeffekt wird dann verwendet, um die Kontur einer größeren Version dieses Zeichens zu strichen:
Ein Großteil der Arbeit in der TextPathEffectPath
Klasse erfolgt in den Feldern und Konstruktoren. Die beiden SKPaint
als Felder definierten Objekte werden für zwei unterschiedliche Zwecke verwendet: Der erste (benannte textPathPaint
) wird verwendet, um das kaufmännische Und-Zeichen mit einem TextSize
Von 50 in einen Pfad für den 1D-Pfadeffekt zu konvertieren. Die zweite (textPaint
) wird verwendet, um die größere Version des kaufmännischen Und-Zeichens mit diesem Pfadeffekt anzuzeigen. Aus diesem Grund wird das Style
zweite Paint-Objekt auf festgelegt, die Eigenschaft wird jedoch StrokeWidth
nicht festgelegtStroke
, da diese Eigenschaft bei Verwendung eines 1D-Pfadeffekts nicht erforderlich ist:
public class TextPathEffectPage : ContentPage
{
const string character = "@";
const float littleSize = 50;
SKPathEffect pathEffect;
SKPaint textPathPaint = new SKPaint
{
TextSize = littleSize
};
SKPaint textPaint = new SKPaint
{
Style = SKPaintStyle.Stroke,
Color = SKColors.Black
};
public TextPathEffectPage()
{
Title = "Text Path Effect";
SKCanvasView canvasView = new SKCanvasView();
canvasView.PaintSurface += OnCanvasViewPaintSurface;
Content = canvasView;
// Get the bounds of textPathPaint
SKRect textPathPaintBounds = new SKRect();
textPathPaint.MeasureText(character, ref textPathPaintBounds);
// Create textPath centered around (0, 0)
SKPath textPath = textPathPaint.GetTextPath(character,
-textPathPaintBounds.MidX,
-textPathPaintBounds.MidY);
// Create the path effect
pathEffect = SKPathEffect.Create1DPath(textPath, littleSize, 0,
SKPath1DPathEffectStyle.Translate);
}
...
}
Der Konstruktor verwendet zunächst das textPathPaint
Objekt zum Messen des kaufmännischen Und-Zeichens mit einer TextSize
Von 50. Die Negativen der Mittelkoordinaten dieses Rechtecks werden dann an die GetTextPath
Methode übergeben, um den Text in einen Pfad zu konvertieren. Der resultierende Pfad weist den Punkt (0, 0) in der Mitte des Zeichens auf, der ideal für einen 1D-Pfadeffekt geeignet ist.
Möglicherweise denken Sie, dass das SKPathEffect
am Ende des Konstruktors erstellte Objekt auf die PathEffect
Eigenschaft textPaint
festgelegt werden könnte, anstatt als Feld zu speichern. Dies funktionierte jedoch nicht sehr gut, da sie die Ergebnisse des MeasureText
Aufrufs im PaintSurface
Handler verzerrte:
public class TextPathEffectPage : ContentPage
{
...
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
// Set textPaint TextSize based on screen size
textPaint.TextSize = Math.Min(info.Width, info.Height);
// Do not measure the text with PathEffect set!
SKRect textBounds = new SKRect();
textPaint.MeasureText(character, ref textBounds);
// Coordinates to center text on screen
float xText = info.Width / 2 - textBounds.MidX;
float yText = info.Height / 2 - textBounds.MidY;
// Set the PathEffect property and display text
textPaint.PathEffect = pathEffect;
canvas.DrawText(character, xText, yText, textPaint);
}
}
Dieser MeasureText
Aufruf wird verwendet, um das Zeichen auf der Seite zu zentrieren. Um Probleme zu vermeiden, wird die PathEffect
Eigenschaft auf das Paint-Objekt festgelegt, nachdem der Text gemessen wurde, aber bevor er angezeigt wird.
Gliederungen von Zeichengliederungen
Normalerweise konvertiert die GetFillPath
Methode einen SKPaint
Pfad in einen anderen durch Anwenden von Paint-Eigenschaften, vor allem die Strichbreite und den Pfadeffekt. Wenn sie ohne Pfadeffekte verwendet wird, wird effektiv ein Pfad erstellt, GetFillPath
der einen anderen Pfad umgibt. Dies wurde auf der Seite "Pfad" im Artikel "Pfadeffekte" auf der Seite "Anzapfen" gezeigt.
Sie können auch den zurückgegebenen GetTextPath
Pfad aufrufenGetFillPath
, aber zunächst sind Sie möglicherweise nicht ganz sicher, wie das aussehen würde.
Auf der Seite "Gliederungslinien zeichen" wird die Technik veranschaulicht. Der gesamte relevante Code befindet sich im PaintSurface
Handler der CharacterOutlineOutlinesPage
Klasse.
Der Konstruktor erstellt zunächst ein SKPaint
Objekt mit textPaint
einer TextSize
Eigenschaft basierend auf der Größe der Seite. Dies wird mithilfe der GetTextPath
Methode in einen Pfad konvertiert. Die Koordinatenargumente, um den Pfad auf dem Bildschirm effektiv zu GetTextPath
zentrieren:
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
using (SKPaint textPaint = new SKPaint())
{
// Set Style for the character outlines
textPaint.Style = SKPaintStyle.Stroke;
// Set TextSize based on screen size
textPaint.TextSize = Math.Min(info.Width, info.Height);
// Measure the text
SKRect textBounds = new SKRect();
textPaint.MeasureText("@", ref textBounds);
// Coordinates to center text on screen
float xText = info.Width / 2 - textBounds.MidX;
float yText = info.Height / 2 - textBounds.MidY;
// Get the path for the character outlines
using (SKPath textPath = textPaint.GetTextPath("@", xText, yText))
{
// Create a new path for the outlines of the path
using (SKPath outlinePath = new SKPath())
{
// Convert the path to the outlines of the stroked path
textPaint.StrokeWidth = 25;
textPaint.GetFillPath(textPath, outlinePath);
// Stroke that new path
using (SKPaint outlinePaint = new SKPaint())
{
outlinePaint.Style = SKPaintStyle.Stroke;
outlinePaint.StrokeWidth = 5;
outlinePaint.Color = SKColors.Red;
canvas.DrawPath(outlinePath, outlinePaint);
}
}
}
}
}
Der PaintSurface
Handler erstellt dann einen neuen Pfad mit dem Namen outlinePath
. Dies wird zum Zielpfad im Aufruf von GetFillPath
. Die StrokeWidth
Eigenschaft von 25 bewirkt outlinePath
, dass die Gliederung eines 25 Pixel breiten Pfads, der die Textzeichen streicht, beschrieben wird. Dieser Pfad wird dann rot mit einer Strichbreite von 5 angezeigt:
Schauen Sie sich genau an, und Sie sehen Überlappungen, wo die Pfadkontur eine scharfe Ecke macht. Dies sind normale Artefakte dieses Prozesses.
Text entlang eines Pfads
Text wird normalerweise auf einer horizontalen Basislinie angezeigt. Text kann vertikal oder diagonal gedreht werden, aber der Basisplan ist immer noch eine gerade Linie.
Es gibt jedoch Zeiten, in denen Text entlang einer Kurve ausgeführt werden soll. Dies ist der Zweck der DrawTextOnPath
Methode von SKCanvas
:
public Void DrawTextOnPath (String text, SKPath path, Single hOffset, Single vOffset, SKPaint paint)
Der im ersten Argument angegebene Text wird so erstellt, dass er entlang des Pfads ausgeführt wird, der als zweites Argument angegeben ist. Sie können den Text mit einem Offset vom Anfang des Pfads mit dem hOffset
Argument beginnen. Normalerweise bildet der Pfad den Basisplan des Texts: Text-Aufsteigende befinden sich auf einer Seite des Pfads, und Textabsteigende befinden sich auf der anderen Seite. Sie können die Textbasislinie jedoch mit dem Argument aus dem vOffset
Pfad versatzen.
Diese Methode bietet keine Möglichkeit, Anleitungen zum Festlegen der TextSize
Eigenschaft SKPaint
bereitzustellen, damit der Text perfekt vom Anfang des Pfads bis zum Ende ausgeführt werden kann. Manchmal können Sie die Größe des Texts selbst ermitteln. In anderen Zeiten müssen Sie Pfadmessfunktionen verwenden, um im nächsten Artikel zu Pfadinformationen und Enumeration beschrieben zu werden.
Das Programm "Zirkeltext " umschließt Text um einen Kreis. Es ist einfach, den Umfang eines Kreises zu bestimmen, sodass es leicht ist, den Text exakt anzupassen. Der PaintSurface
Handler der CircularTextPage
Klasse berechnet einen Radius eines Kreises basierend auf der Größe der Seite. Dieser Kreis wird:circularPath
public class CircularTextPage : ContentPage
{
const string text = "xt in a circle that shapes the te";
...
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
using (SKPath circularPath = new SKPath())
{
float radius = 0.35f * Math.Min(info.Width, info.Height);
circularPath.AddCircle(info.Width / 2, info.Height / 2, radius);
using (SKPaint textPaint = new SKPaint())
{
textPaint.TextSize = 100;
float textWidth = textPaint.MeasureText(text);
textPaint.TextSize *= 2 * 3.14f * radius / textWidth;
canvas.DrawTextOnPath(text, circularPath, 0, 0, textPaint);
}
}
}
}
Die TextSize
Eigenschaft wird textPaint
dann so angepasst, dass die Textbreite dem Umfang des Kreises entspricht:
Der Text selbst wurde auch als kreisförmig gewählt: Das Wort "circle" ist sowohl das Thema des Satzes als auch das Objekt eines präpositionalen Ausdrucks.