Percorsi e testo in SkiaSharp
Esplorare l'intersezione di percorsi e testo
Nei sistemi grafici moderni, i tipi di carattere di testo sono raccolte di contorni di caratteri, in genere definiti da curve quadratiche di Bézier. Di conseguenza, molti sistemi grafici moderni includono una funzionalità per convertire i caratteri di testo in un percorso grafico.
Si è già visto che è possibile tracciare i contorni dei caratteri di testo e riempirli. In questo modo è possibile visualizzare questi contorni di caratteri con una particolare larghezza del tratto e anche un effetto di percorso, come descritto nell'articolo Effetti percorso. Ma è anche possibile convertire una stringa di caratteri in un SKPath
oggetto . Ciò significa che i contorni di testo possono essere usati per ritagliare con tecniche descritte nell'articolo Ritaglio con percorsi e aree geografiche .
Oltre a usare un effetto di percorso per tracciare una struttura di caratteri, è anche possibile creare effetti di percorso basati su un percorso derivato da una stringa di caratteri e anche combinare i due effetti:
Nell'articolo precedente sugli effetti percorso è stato illustrato come il GetFillPath
metodo di SKPaint
può ottenere una struttura di un percorso tracciato. È anche possibile usare questo metodo con percorsi derivati da contorni di caratteri.
Infine, questo articolo illustra un'altra intersezione di percorsi e testo: il DrawTextOnPath
metodo di consente di SKCanvas
visualizzare una stringa di testo in modo che la linea di base del testo segua un percorso curvo.
Conversione da testo a percorso
Il GetTextPath
metodo di converte una stringa di SKPaint
caratteri in un SKPath
oggetto :
public SKPath GetTextPath (String text, Single x, Single y)
Gli x
argomenti e y
indicano il punto iniziale della linea di base del lato sinistro del testo. Svolgono lo stesso ruolo qui come nel DrawText
metodo di SKCanvas
. All'interno del percorso, la linea di base del lato sinistro del testo avrà le coordinate (x, y).
Il GetTextPath
metodo è eccessivo se si vuole semplicemente riempire o tracciare il percorso risultante. Il metodo normale DrawText
consente di eseguire questa operazione. Il GetTextPath
metodo è più utile per altre attività che coinvolgono percorsi.
Una di queste attività è ritagliare. La pagina Ritaglio testo crea un percorso di ritaglio in base ai contorni di caratteri della parola "CODE". Questo percorso viene esteso alle dimensioni della pagina per ritagliare una bitmap contenente un'immagine del codice sorgente del testo di ritaglio:
Il ClippingTextPage
costruttore della classe carica la bitmap archiviata come risorsa incorporata nella cartella Media della soluzione:
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);
}
}
...
}
Il PaintSurface
gestore inizia creando un SKPaint
oggetto adatto per il testo. La Typeface
proprietà è impostata e , TextSize
anche se per questa particolare applicazione la TextSize
proprietà è puramente arbitraria. Si noti anche che non esiste alcuna Style
impostazione.
Le impostazioni delle TextSize
proprietà e Style
non sono necessarie perché questo SKPaint
oggetto viene utilizzato esclusivamente per la GetTextPath
chiamata usando la stringa di testo "CODE". Il gestore misura quindi l'oggetto risultante SKPath
e applica tre trasformazioni al centro e la ridimensiona alle dimensioni della pagina. Il percorso può quindi essere impostato come percorso di ritaglio:
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)));
}
}
Una volta impostato il percorso di ritaglio, la bitmap può essere visualizzata e verrà ritagliata nei contorni dei caratteri. Si noti l'uso del AspectFill
metodo di SKRect
che calcola un rettangolo per riempire la pagina mantenendo le proporzioni.
La pagina Effetto percorso testo converte un singolo carattere e commerciale in un percorso per creare un effetto percorso 1D. Un oggetto paint con questo effetto di percorso viene quindi utilizzato per tracciare il contorno di una versione più grande dello stesso carattere:
Gran parte del lavoro nella TextPathEffectPath
classe si verifica nei campi e nel costruttore. I due SKPaint
oggetti definiti come campi vengono usati per due scopi diversi: il primo (denominato textPathPaint
) viene usato per convertire la e commerciale con un TextSize
valore pari a 50 in un percorso per l'effetto del percorso 1D. Il secondo (textPaint
) viene usato per visualizzare la versione più grande dell'e commerciale con tale effetto di percorso. Per questo motivo, l'oggetto Style
di questo secondo oggetto paint è impostato su Stroke
, ma la StrokeWidth
proprietà non è impostata perché tale proprietà non è necessaria quando si usa un effetto di percorso 1D:
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);
}
...
}
Il costruttore usa prima l'oggetto textPathPaint
per misurare la e commerciale con un TextSize
valore pari a 50. I negativi delle coordinate centrale del rettangolo vengono quindi passati al GetTextPath
metodo per convertire il testo in un percorso. Il percorso risultante ha il punto (0, 0) al centro del carattere, ideale per un effetto di percorso 1D.
Si potrebbe pensare che l'oggetto SKPathEffect
creato alla fine del costruttore possa essere impostato sulla PathEffect
proprietà di textPaint
anziché salvarlo come campo. Ma questo si è rivelato non funzionare molto bene perché ha distorto i risultati della MeasureText
chiamata nel PaintSurface
gestore:
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);
}
}
Tale MeasureText
chiamata viene usata per centrare il carattere nella pagina. Per evitare problemi, la PathEffect
proprietà viene impostata sull'oggetto paint dopo che il testo è stato misurato, ma prima che venga visualizzato.
Contorni di contorni di caratteri
In genere il GetFillPath
metodo di SKPaint
converte un percorso in un altro applicando proprietà di vernice, in particolare la larghezza del tratto e l'effetto percorso. Se usato senza effetti di percorso, GetFillPath
crea in modo efficace un percorso che delinea un altro percorso. Questa operazione è stata illustrata nella pagina Tap to Outline the Path nell'articolo Effetti percorso.
È anche possibile chiamare GetFillPath
sul percorso restituito da GetTextPath
, ma in un primo momento potrebbe non essere completamente sicuro di ciò che sarebbe simile.
La pagina Contorni struttura carattere illustra la tecnica. Tutto il codice pertinente si trova nel PaintSurface
gestore della CharacterOutlineOutlinesPage
classe .
Il costruttore inizia creando un SKPaint
oggetto denominato textPaint
con una TextSize
proprietà in base alle dimensioni della pagina. Questa operazione viene convertita in un percorso usando il GetTextPath
metodo . Gli argomenti delle coordinate per GetTextPath
centrare efficacemente il percorso sullo schermo:
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);
}
}
}
}
}
Il PaintSurface
gestore crea quindi un nuovo percorso denominato outlinePath
. Questo diventa il percorso di destinazione nella chiamata a GetFillPath
. La StrokeWidth
proprietà di 25 causa outlinePath
la descrizione del contorno di un percorso a 25 pixel che strozza i caratteri di testo. Questo percorso viene quindi visualizzato in rosso con una larghezza del tratto pari a 5:
Guarda attentamente e vedrai le sovrapposizioni in cui il contorno del percorso crea un angolo acuto. Si tratta di artefatti normali di questo processo.
Testo lungo un percorso
Il testo viene in genere visualizzato in una linea di base orizzontale. Il testo può essere ruotato per essere eseguito verticalmente o diagonalmente, ma la linea di base è ancora una linea retta.
In alcuni casi, tuttavia, quando si vuole che il testo venga eseguito lungo una curva. Questo è lo scopo del DrawTextOnPath
metodo di SKCanvas
:
public Void DrawTextOnPath (String text, SKPath path, Single hOffset, Single vOffset, SKPaint paint)
Il testo specificato nel primo argomento viene eseguito lungo il percorso specificato come secondo argomento. È possibile iniziare il testo in corrispondenza di un offset dall'inizio del percorso con l'argomento hOffset
. In genere il percorso costituisce la linea di base del testo: gli ascendenti di testo si trovano su un lato del percorso e i discendenti di testo si trovano sull'altro. È tuttavia possibile eseguire l'offset della linea di base del testo dal percorso con l'argomento vOffset
.
Questo metodo non ha alcuna funzionalità per fornire indicazioni sull'impostazione della TextSize
proprietà di SKPaint
per rendere il testo perfettamente in esecuzione dall'inizio del percorso alla fine. A volte è possibile capire che le dimensioni del testo sono personalizzate. In altri casi è necessario usare le funzioni di misurazione del percorso da descrivere nell'articolo successivo informazioni sul percorso ed enumerazione.
Il programma Testo circolare esegue il wrapping del testo intorno a un cerchio. È facile determinare la circonferenza di un cerchio, quindi è facile ridimensionare il testo in modo preciso. Il PaintSurface
gestore della CircularTextPage
classe calcola un raggio di un cerchio in base alle dimensioni della pagina. Il cerchio diventa 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);
}
}
}
}
La TextSize
proprietà di textPaint
viene quindi regolata in modo che la larghezza del testo corrisponda alla circonferenza del cerchio:
Il testo stesso è stato scelto anche come circolare: la parola "cerchio" è sia l'oggetto della frase che l'oggetto di una frase preposizionale.