Udostępnij za pośrednictwem


Ścieżki i tekst w skiaSharp

Eksplorowanie przecięć ścieżek i tekstu

W nowoczesnych systemach graficznych czcionki tekstowe to kolekcje konturów znaków, zwykle definiowane przez krzywe kwadratowe Béziera. W związku z tym wiele nowoczesnych systemów graficznych obejmuje obiekt umożliwiający konwertowanie znaków tekstowych na ścieżkę graficzną.

Wiesz już, że można pociągnić kontury znaków tekstowych, a także je wypełnić. Dzięki temu można wyświetlić te kontury znaków o określonej szerokości pociągnięcia, a nawet efekt ścieżki, jak opisano w artykule Path Effects. Można jednak również przekonwertować ciąg znaków na SKPath obiekt. Oznacza to, że kontury tekstowe mogą służyć do tworzenia wycinków za pomocą technik opisanych w artykule Tworzenie wycinków ze ścieżkami i regionami .

Oprócz użycia efektu ścieżki do pociągnięcia konturu znaku, można również utworzyć efekty ścieżki oparte na ścieżce pochodzącej z ciągu znaku, a nawet połączyć dwa efekty:

Efekt ścieżki tekstowej

W poprzednim artykule dotyczącym funkcji Path Effects pokazano, jak GetFillPath metoda SKPaint umożliwia uzyskanie konspektu ścieżki pociągniętej. Można również użyć tej metody ze ścieżkami pochodzącymi z konspektów znaków.

Na koniec w tym artykule przedstawiono kolejne przecięcie ścieżek i tekstu: DrawTextOnPath Metoda SKCanvas umożliwia wyświetlenie ciągu tekstowego tak, aby punkt odniesienia tekstu był zgodny ze ścieżką zakrzywioną.

Konwersja tekstu na ścieżkę

Metoda GetTextPath konwertowania SKPaint ciągu znaków na SKPath obiekt:

public SKPath GetTextPath (String text, Single x, Single y)

Argumenty x i y wskazują punkt początkowy punktu odniesienia po lewej stronie tekstu. Odgrywają tu tę samą rolę co w DrawText metodzie SKCanvas. W ścieżce linia bazowa po lewej stronie tekstu będzie miała współrzędne (x, y).

Metoda GetTextPath jest nadmierna, jeśli chcesz tylko wypełnić lub pociągnić wynikową ścieżkę. DrawText Normalna metoda pozwala to zrobić. Metoda jest bardziej przydatna GetTextPath w przypadku innych zadań obejmujących ścieżki.

Jednym z tych zadań jest wycinkowanie. Strona Clipping Text tworzy ścieżkę do wycinków na podstawie konspektu znaku "CODE". Ta ścieżka jest rozciągnięta do rozmiaru strony, aby wyciąć mapę bitową zawierającą obraz kodu źródłowego Clipping Text :

Potrójny zrzut ekranu przedstawiający stronę Przycinanie tekstu

Konstruktor ClippingTextPage klasy ładuje mapę bitową przechowywaną jako zasób osadzony w folderze Media rozwiązania:

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);
        }
    }
    ...
}

Procedura PaintSurface obsługi rozpoczyna się od utworzenia obiektu odpowiedniego SKPaint dla tekstu. Właściwość Typeface jest ustawiona, a także TextSizewłaściwość , chociaż dla tej konkretnej aplikacji TextSize właściwość jest czysto arbitralna. Zwróć również uwagę, że nie Style ma żadnego ustawienia.

TextSize Ustawienia właściwości i Style nie są niezbędne, ponieważ ten SKPaint obiekt jest używany wyłącznie dla GetTextPath wywołania przy użyciu ciągu tekstowego "CODE". Następnie procedura obsługi mierzy wynikowy SKPath obiekt i stosuje trzy przekształcenia, aby go wyśrodkować i skalować do rozmiaru strony. Następnie ścieżkę można ustawić jako ścieżkę wycinkową:

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)));
    }
}

Po ustawieniu ścieżki wycinków można wyświetlić mapę bitową i zostanie obcięta do obramowania znaków. Zwróć uwagę na użycie AspectFill metody SKRect , która oblicza prostokąt do wypełniania strony przy zachowaniu współczynnika proporcji.

Strona Efekt ścieżki tekstowej konwertuje pojedynczy znak ampersand na ścieżkę w celu utworzenia efektu ścieżki 1D. Obiekt farby z tym efektem ścieżki jest następnie używany do pociągnięcia konturu większej wersji tego samego znaku:

Potrójny zrzut ekranu przedstawiający stronę efektu ścieżki tekstu

Większość pracy w TextPathEffectPath klasie odbywa się w polach i konstruktorze. Dwa SKPaint obiekty zdefiniowane jako pola są używane do dwóch różnych celów: pierwszy (nazwany textPathPaint) służy do konwertowania znaku ampersand z wartością TextSize 50 na ścieżkę dla efektu ścieżki 1D. Drugi (textPaint) służy do wyświetlania większej wersji znaku ampersand z tym efektem ścieżki. Z tego powodu drugi obiekt farby jest ustawiony na Stroke, ale StrokeWidth właściwość nie jest ustawiona, Style ponieważ ta właściwość nie jest konieczna w przypadku używania efektu ścieżki 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);
    }
    ...
}

Konstruktor najpierw używa textPathPaint obiektu do mierzenia ampersand z wartością TextSize 50. Wartości ujemne współrzędnych środkowych tego prostokąta są następnie przekazywane do GetTextPath metody w celu przekonwertowania tekstu na ścieżkę. Wynikowa ścieżka ma punkt (0, 0) w środku znaku, który jest idealny dla efektu ścieżki 1D.

Można pomyśleć, że SKPathEffect obiekt utworzony na końcu konstruktora może być ustawiony na właściwość textPaint zamiast zapisywać PathEffect jako pole. Okazało się jednak, że nie działa to bardzo dobrze, ponieważ zniekształciło wyniki MeasureText wywołania w procedurze PaintSurface obsługi:

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);
    }
}

To MeasureText wywołanie służy do wyśrodkowania znaku na stronie. Aby uniknąć problemów, PathEffect właściwość jest ustawiona na obiekt farby po zmierzeniu tekstu, ale przed jego wyświetleniem.

Kontury konspektów znaków

GetFillPath Zwykle metoda konwersji SKPaint jednej ścieżki na inną przez zastosowanie właściwości farby, zwłaszcza efektu szerokości pociągnięcia i ścieżki. W przypadku użycia bez efektów GetFillPath ścieżki skutecznie tworzy ścieżkę, która przedstawia inną ścieżkę. Zostało to pokazane na stronie Naciśnij, aby nakreślić ścieżkę w artykule Path Effects (Efekty ścieżki).

Możesz również wywołać GetFillPath ścieżkę zwróconą z GetTextPath , ale na początku może nie być całkowicie pewien, jak to wygląda.

Na stronie Konspektu znaku przedstawiono technikę. Cały odpowiedni kod znajduje się w procedurze PaintSurfaceCharacterOutlineOutlinesPage obsługi klasy .

Konstruktor rozpoczyna się od utworzenia SKPaint obiektu o nazwie textPaint z właściwością TextSize na podstawie rozmiaru strony. Jest on konwertowany na ścieżkę przy użyciu GetTextPath metody . Argumenty współrzędnych, aby GetTextPath skutecznie wyśrodkować ścieżkę na ekranie:

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);
                }
            }
        }
    }
}

Następnie PaintSurface program obsługi tworzy nową ścieżkę o nazwie outlinePath. Staje się to ścieżką docelową w wywołaniu metody GetFillPath. Właściwość StrokeWidth 25 powoduje outlinePath opisanie konturu ścieżki o szerokości 25 pikseli, która nabija znaki tekstowe. Ta ścieżka jest następnie wyświetlana na czerwono z szerokością pociągnięcia 5:

Potrójny zrzut ekranu przedstawiający stronę Konspektu znaku

Przyjrzyj się bliżej i zobaczysz nakładające się na siebie krawędzie ścieżki. Są to normalne artefakty tego procesu.

Tekst wzdłuż ścieżki

Tekst jest zwykle wyświetlany w poziomie linii bazowej. Tekst można obracać w pionie lub po przekątnej, ale linia bazowa jest nadal prostą linią.

Czasami jednak tekst ma być uruchamiany wzdłuż krzywej. Jest to cel DrawTextOnPath metody :SKCanvas

public Void DrawTextOnPath (String text, SKPath path, Single hOffset, Single vOffset, SKPaint paint)

Tekst określony w pierwszym argumencie jest uruchamiany wzdłuż ścieżki określonej jako drugi argument. Tekst można rozpocząć od przesunięcia od początku ścieżki z argumentem hOffset . Zwykle ścieżka tworzy punkt odniesienia tekstu: Rosnąco tekst znajdują się po jednej stronie ścieżki, a malejąco tekstu znajdują się na drugiej. Można jednak zrównoważyć linię bazową tekstu ze ścieżki argumentem vOffset .

Ta metoda nie ma możliwości zapewnienia wskazówek dotyczących ustawiania TextSize właściwości SKPaint , aby rozmiar tekstu był idealnie uruchamiany od początku ścieżki do końca. Czasami można samodzielnie ustalić ten rozmiar tekstu. Innym razem należy użyć funkcji pomiaru ścieżki, które mają być opisane w następnym artykule w temacie Informacje o ścieżce i wyliczenie.

Program Okrągły tekst owija tekst wokół okręgu. Łatwo jest określić obwód okręgu, dzięki czemu można łatwo dopasować tekst do dokładnego dopasowania. Procedura PaintSurface obsługi CircularTextPage klasy oblicza promień okręgu na podstawie rozmiaru strony. Ten okrąg staje się :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);
            }
        }
    }
}

TextSize Właściwość textPaint elementu jest następnie dopasowywana tak, aby szerokość tekstu odpowiadała obwodowi okręgu:

Potrójny zrzut ekranu przedstawiający stronę Tekst okrągły

Sam tekst został wybrany jako nieco okrągły, jak również: Słowo "circle" jest zarówno przedmiotem zdania, jak i obiektem frazy przyimkowej.