Sdílet prostřednictvím


Cesty a text v SkiaSharpu

Prozkoumání průniku cest a textu

V moderních grafických systémech jsou textová písma kolekce obrysů znaků, obvykle definované kvadratickými Bézierovými křivkami. V důsledku toho mnoho moderních grafických systémů zahrnuje zařízení pro převod textových znaků na grafickou cestu.

Už jste viděli, že můžete tahat obrysy textových znaků a vyplnit je. Díky tomu můžete tyto obrysy znaků zobrazit s konkrétní šířkou tahu a dokonce i efektem cesty, jak je popsáno v článku Efekty cesty. Je ale také možné převést řetězec znaků na SKPath objekt. To znamená, že osnovy textu se dají použít k výřezu pomocí technik popsaných v článku Výřez s cestami a oblastmi.

Kromě použití efektu cesty k tahu obrysu znaku můžete také vytvořit efekty cesty založené na cestě odvozené z řetězce znaků a můžete dokonce zkombinovat tyto dva efekty:

Efekt cesty k textu

V předchozím článku o efektech cesty jste viděli, jak GetFillPath metoda SKPaint může získat osnovu tahové cesty. Tuto metodu můžete použít také s cestami odvozenými z obrysů znaků.

Nakonec tento článek ukazuje další průnik cest a textu: DrawTextOnPath Metoda SKCanvas umožňuje zobrazit textový řetězec tak, aby směrný plán textu následuje zakřivenou cestou.

Převod textu na cestu

GetTextPath Metoda SKPaint převede řetězec znaků na SKPath objekt:

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

y Argumenty x označují výchozí bod účaří levé strany textu. Hrají zde stejnou roli jako v DrawText metodě SKCanvas. V cestě bude mít účaří levé strany textu souřadnice (x, y).

Metoda GetTextPath je nadměrná, pokud chcete pouze vyplnit nebo tahnout výslednou cestu. DrawText Normální metoda vám to umožní. Metoda GetTextPath je užitečnější pro jiné úlohy zahrnující cesty.

Jedním z těchto úkolů je výřez. Stránka Výřez textu vytvoří cestu výřezu na základě obrysů znaků slova "KÓD". Tato cesta se roztáhne na velikost stránky a vystřihuje rastrový obrázek, který obsahuje obrázek zdrojového kódu Oříznutí textu :

Trojitý snímek obrazovky se stránkou Oříznutí textu

Konstruktor ClippingTextPage třídy načte rastrový obrázek, který je uložen jako vložený prostředek ve složce Media řešení:

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

Obslužná rutina PaintSurface začíná vytvořením objektu vhodného SKPaint pro text. Vlastnost Typeface je nastavena i TextSize, i když pro tuto konkrétní aplikaci TextSize je vlastnost čistě libovolná. Všimněte si také, že neexistuje žádné Style nastavení.

Nastavení TextSize a Style vlastnosti nejsou nutné, protože tento SKPaint objekt se používá výhradně pro GetTextPath volání pomocí textového řetězce "CODE". Obslužná rutina pak změruje výsledný SKPath objekt a použije tři transformace na střed a škáluje ho na velikost stránky. Tuto cestu pak můžete nastavit jako cestu výřezu:

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 nastavení cesty výřezu se dá rastrový obrázek zobrazit a vystřihuje se na obrysy znaků. Všimněte si použití AspectFill metody SKRect , která vypočítá obdélník pro vyplnění stránky při zachování poměru stran.

Stránka Efektu textové cesty převede jeden ampersand znak na cestu a vytvoří 1D efekt cesty. Malování objektu s tímto efektem cesty se pak použije k tahu obrysu větší verze stejného znaku:

Trojitý snímek obrazovky se stránkou efektu cesty k textu

Velká část práce ve TextPathEffectPath třídě se vyskytuje v polích a konstruktoru. Dva SKPaint objekty definované jako pole se používají pro dva různé účely: První (pojmenovaný textPathPaint) slouží k převodu ampersandu TextSize s hodnotou 50 na cestu pro efekt 1D cesty. Druhá (textPaint) slouží k zobrazení větší verze ampersandu s tímto efektem cesty. Z tohoto důvodu Style je druhý objekt malování nastaven na Stroke, ale StrokeWidth vlastnost není nastavena, protože tato vlastnost není nutná při použití efektu cesty 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 nejprve použije textPathPaint objekt k měření ampersandu TextSize s 50. Zápory středových souřadnic tohoto obdélníku se pak předají GetTextPath metodě pro převod textu na cestu. Výsledná cesta má bod (0, 0) uprostřed znaku, který je ideální pro efekt 1D cesty.

Můžete si myslet, že SKPathEffect objekt vytvořený na konci konstruktoru může být nastaven na PathEffect vlastnost textPaint namísto uložení jako pole. Ale ukázalo se, že to nefunguje velmi dobře, protože zkresloval výsledky MeasureText volání v obslužné rutině PaintSurface :

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

Toto MeasureText volání slouží ke středu znaku na stránce. Aby nedocházelo k problémům, PathEffect vlastnost je nastavena na objekt malování po měření textu, ale před zobrazením.

Obrysy obrysů znaků

GetFillPathSKPaint Metoda obvykle převede jednu cestu na jinou použitím vlastností barvy, zejména šířky tahu a efekt cesty. Při použití bez efektů cesty efektivně vytvoří cestu, GetFillPath která nastíní jinou cestu. To jsme si ukázali v článku Efekty cesty v části Klepnutím na osnovu stránky Cesta.

Můžete také volat GetFillPath cestu vrácenou z GetTextPath cesty, ale zpočátku si nejste úplně jistí, jak by to vypadalo.

Stránka Osnovy obrysů znaků ukazuje techniku. Veškerý relevantní kód je v PaintSurface obslužné rutině CharacterOutlineOutlinesPage třídy.

Konstruktor začíná vytvořením objektu SKPaint pojmenovaného textPaint vlastností TextSize na základě velikosti stránky. To je převedeno na cestu pomocí GetTextPath metody. Argumenty souřadnic, které efektivně zacentrují GetTextPath cestu na obrazovce:

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

Obslužná rutina PaintSurface pak vytvoří novou cestu s názvem outlinePath. Tím se stane cílová cesta v volání GetFillPath. Vlastnost StrokeWidth 25 způsobí outlinePath popis obrysu cesty s šířkou 25 pixelů s textem. Tato cesta se pak zobrazí červeně s šířkou tahu 5:

Trojitý snímek obrazovky se stránkou Osnovy osnovy znaků

Podívejte se pozorně a uvidíte, že se překrývají, kde obrys cesty dělá ostrý roh. Jedná se o běžné artefakty tohoto procesu.

Text podél cesty

Text se obvykle zobrazuje na vodorovném účaří. Text lze otočit tak, aby běžel svisle nebo diagonálně, ale směrný plán je stále přímkou.

Někdy ale platí, že když chcete, aby text běžel podél křivky. Jedná se o účel DrawTextOnPath metody SKCanvas:

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

Text zadaný v prvním argumentu se provede tak, aby běžel podél cesty zadané jako druhý argument. Text můžete zahájit od začátku cesty argumentem hOffset . Cesta obvykle tvoří směrný plán textu: Vzestupné texty jsou na jedné straně cesty a sestupné texty jsou na druhé. Směrný plán textu ale můžete odsazením cesty odsazením argumentu vOffset .

Tato metoda nemá žádné možnosti, jak poskytnout pokyny k nastavení TextSize vlastnosti SKPaint , aby text byl dokonale nastaven tak, aby běžel od začátku cesty na konec. Někdy můžete zjistit, že velikost textu je sama o sobě. Jindy budete muset použít funkce měření cesty, které jsou popsány v dalším článku o informacích o cestě a výčtu.

Program Kruhový text zalamuje text kolem kruhu. Je snadné určit obvod kruhu, takže je snadné upravit velikost textu tak, aby se přesně vešel. Obslužná PaintSurface rutina CircularTextPage třídy vypočítá poloměr kruhu na základě velikosti stránky. Tento kruh se stane 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);
            }
        }
    }
}

Vlastnost TextSizetextPaint se pak upraví tak, aby šířka textu odpovídala obvodu kruhu:

Trojitý snímek obrazovky se stránkou Kruhový text

Samotný text byl zvolen tak, aby byl poněkud kruhový: Slovo "kruh" je předmětem věty i objektu předložkové fráze.