Percorsi e testo in SkiaSharp

Download Sample Scaricare l'esempio

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:

Text Path Effect

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:

Triple screenshot of the Clipping Text page

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 , TextSizeanche 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:

Triple screenshot of the Text Path Effect page

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:

Triple screenshot of the Character Outline Outlines page

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:

Triple screenshot of the Circular Text page

Il testo stesso è stato scelto anche come circolare: la parola "cerchio" è sia l'oggetto della frase che l'oggetto di una frase preposizionale.