SkiaSharp의 경로 및 텍스트

Download Sample 샘플 다운로드

경로와 텍스트의 교차점 살펴보기

최신 그래픽 시스템에서 텍스트 글꼴은 문자 윤곽선의 컬렉션으로, 일반적으로 4차원 베지어 곡선으로 정의됩니다. 따라서 많은 최신 그래픽 시스템에는 텍스트 문자를 그래픽 경로로 변환하는 기능이 포함되어 있습니다.

텍스트 문자의 윤곽선을 스트로크하고 채울 수 있다는 것을 이미 확인했습니다. 이렇게 하면 경로 효과 문서에 설명된 대로 특정 스트로크 너비와 경로 효과로 이러한 문자 윤곽선을 표시할 수 있습니다 . 그러나 문자열을 개체로 변환할 수도 있습니다 SKPath . 즉, 경로 및 영역이 있는 클리핑 문서에서 설명 한 기술을 사용하여 텍스트 개요를 클리핑하는 데 사용할 수 있습니다 .

경로 효과를 사용하여 문자 윤곽선을 스트로크하는 것 외에도 문자 문자열에서 파생된 경로를 기반으로 하는 경로 효과를 만들 수 있으며 두 효과를 결합할 수도 있습니다.

Text Path Effect

경로 효과에 대한 이전 문서에서는 메서드가 GetFillPath 스트로크된 경로의 SKPaint 윤곽선을 가져오는 방법을 알아보았습니다. 문자 윤곽선에서 파생된 경로와 함께 이 메서드를 사용할 수도 있습니다.

마지막으로 이 문서에서는 경로와 텍스트의 또 다른 교집합을 보여 줍니다. 이 메서드를 DrawTextOnPath 사용하면 텍스트의 SKCanvas 기준선이 곡선 경로를 따르도록 텍스트 문자열을 표시할 수 있습니다.

텍스트에서 경로로 변환

GetTextPath 문자열을 개체로 변환하는 SKPath 메서드 SKPaint 입니다.

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

y 인수는 x 텍스트 왼쪽 기준선의 시작점을 나타냅니다. 여기서는 .의 SKCanvas메서드와 DrawText 동일한 역할을 합니다. 경로 내에서 텍스트 왼쪽의 기준선에는 좌표(x, y)가 있습니다.

GetTextPath 결과 경로를 채우거나 스트로크하려는 경우 메서드가 과잉입니다. 일반 DrawText 메서드를 사용하면 이 작업을 수행할 수 있습니다. 이 GetTextPath 메서드는 경로와 관련된 다른 작업에 더 유용합니다.

이러한 작업 중 하나는 클리핑입니다. 텍스트 클리핑 페이지에서는 "CODE"라는 단어의 문자 윤곽선을 기반으로 클리핑 경로를 만듭니다. 이 경로는 페이지 크기로 확장되어 클리핑 텍스트 소스 코드의 이미지가 포함된 비트맵을 클리핑합니다.

Triple screenshot of the Clipping Text page

ClippingTextPage 클래스 생성자는 솔루션의 Media 폴더에 포함된 리소스로 저장된 비트맵을 로드합니다.

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

PaintSurface 처리기는 텍스트에 적합한 개체를 SKPaint 만들어 시작합니다. 이 특정 애플리케이션 TextSizeTypeface 경우 속성은 순수하게 임의TextSize이지만 속성도 설정됩니다. 또한 설정이 없습니다 Style .

이 개체는 TextSize 텍스트 문자열 "CODE"를 사용하는 호출에 GetTextPath 만 사용되므로 속성 설정이 Style 필요하지 SKPaint 않습니다. 그런 다음 처리기는 결과 SKPath 개체를 측정하고 세 개의 변환을 적용하여 가운데에 적용하고 페이지 크기로 크기를 조정합니다. 그런 다음, 경로를 클리핑 경로로 설정할 수 있습니다.

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

클리핑 경로가 설정되면 비트맵을 표시할 수 있으며 문자 윤곽선으로 잘립니다. 가로 세로 비율을 유지하면서 페이지를 채우기 위한 사각형을 계산하는 메서드 SKRect 를 사용합니다AspectFill.

텍스트 경로 효과 페이지는 단일 앰퍼샌드 문자를 경로로 변환하여 1D 경로 효과를 만듭니다. 그런 다음 이 경로 효과가 있는 페인트 개체를 사용하여 동일한 문자의 더 큰 버전의 윤곽선을 스트로크합니다.

Triple screenshot of the Text Path Effect page

클래스의 대부분의 작업은 TextPathEffectPath 필드 및 생성자에서 발생합니다. 필드로 정의된 두 SKPaint 개체는 두 가지 용도로 사용됩니다. 첫 번째 개체(명명됨 textPathPaint)는 1D 경로 효과에 대한 경로로 TextSize 앰퍼샌드를 50으로 변환하는 데 사용됩니다. 두 번째(textPaint)는 더 큰 버전의 앰퍼샌드를 해당 경로 효과와 함께 표시하는 데 사용됩니다. 이러한 이유로 Style 이 두 번째 페인트 개체는 설정 Stroke되지만 StrokeWidth 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);
    }
    ...
}

생성자는 먼저 개체를 textPathPaint 사용하여 50의 앰퍼샌드를 TextSize 측정합니다. 그런 다음 해당 사각형의 가운데 좌표의 음수는 메서드에 GetTextPath 전달되어 텍스트를 경로로 변환합니다. 결과 경로는 문자의 가운데에 (0, 0) 점을 하며 1D 경로 효과에 적합합니다.

생성자의 끝에서 만든 개체를 필드로 저장하지 않고 속성 textPaint 으로 설정할 PathEffect 수 있다고 생각할 SKPathEffect 수 있습니다. 그러나 처리기에서 호출 결과를 MeasureText 왜곡했기 때문에 제대로 작동하지 않는 것으로 밝혀졌습니다 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);
    }
}

MeasureText 호출은 페이지의 문자를 가운데에 배치하는 데 사용됩니다. 문제를 PathEffect 방지하기 위해 텍스트가 측정된 후와 표시되기 전에 속성이 페인트 개체로 설정됩니다.

문자 윤곽선의 개요

일반적으로 메서드 SKPaintGetFillPath 페인트 속성, 특히 스트로크 너비 및 경로 효과를 적용하여 한 경로를 다른 경로로 변환합니다. 경로 효과 GetFillPath 없이 사용할 경우 다른 경로를 간략하게 설명하는 경로를 효과적으로 만듭니다. 이는 경로 효과 문서의 경로 개요 탭 페이지에서 설명되었습니다.

반환 GetTextPath 된 경로를 호출 GetFillPath 할 수도 있지만 처음에는 어떤 모습일지 완전히 확신할 수 없습니다.

문자 개요 페이지는 이 기술을 보여 줍니다. 모든 관련 코드는 PaintSurface 클래스의 처리기에 있습니다 CharacterOutlineOutlinesPage .

생성자는 페이지 크기에 따라 속성으로 TextSize 명명된 SKPainttextPaint 개체를 만들어 시작합니다. 메서드를 사용하여 경로로 변환됩니다 GetTextPath . 화면의 경로를 효과적으로 가운데에 배치하는 GetTextPath 좌표 인수입니다.

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

PaintSurface 그런 다음 처리기는 이름이 outlinePath새 경로를 만듭니다. 이 경로는 호출에서 대상 경로가 GetFillPath됩니다. 25의 속성은 StrokeWidthoutlinePath 텍스트 문자를 쓰다듬는 25픽셀 너비 경로의 윤곽선을 설명합니다. 그런 다음 이 경로는 스트로크 너비가 5인 빨간색으로 표시됩니다.

Triple screenshot of the Character Outline Outlines page

자세히 살펴보면 경로 윤곽선이 날카로운 모서리를 만드는 겹치는 것을 볼 수 있습니다. 이러한 아티팩트가 이 프로세스의 일반적인 아티팩트입니다.

경로를 따라 텍스트

텍스트는 일반적으로 가로 기준선에 표시됩니다. 텍스트를 세로 또는 대각선으로 실행하도록 회전할 수 있지만 기준선은 여전히 직선입니다.

그러나 곡선을 따라 텍스트를 실행하려는 경우가 있습니다. 이는 다음 메서드SKCanvas의 용도입니다DrawTextOnPath.

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

첫 번째 인수에 지정된 텍스트는 두 번째 인수로 지정된 경로를 따라 실행되도록 만들어집니다. 인수를 사용하여 경로 hOffset 의 시작 부분에서 오프셋으로 텍스트를 시작할 수 있습니다. 일반적으로 경로는 텍스트의 기준선을 형성합니다. 텍스트 오름차순은 경로의 한쪽에 있고 텍스트 하위 항목은 다른 쪽에 있습니다. 그러나 인수를 사용하여 경로에서 텍스트 기준을 오프셋할 vOffset 수 있습니다.

이 메서드에는 경로의 시작 부분에서 끝까지 실행되도록 텍스트 크기를 완벽하게 조정하기 위한 속성을 SKPaint 설정하는 TextSize 방법에 대한 지침을 제공할 수 있는 기능이 없습니다. 경우에 따라 해당 텍스트 크기를 직접 파악할 수 있습니다. 경로 정보 및 열거형에 대한 다음 문서에서 설명하는 경로 측정 함수를 사용해야 하는 경우도 있습니다.

원형 텍스트 프로그램은 원을 중심으로 텍스트를 줄 바꿈합니다. 원의 둘레를 쉽게 확인할 수 있으므로 텍스트 크기를 정확하게 조정하기 쉽습니다. PaintSurface 클래스의 CircularTextPage 처리기는 페이지 크기에 따라 원 반경을 계산합니다. 해당 원은 다음과 같이 됩니다.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 그런 다음 텍스트 너비가 원의 둘레와 일치하게 속성 textPaint 이 조정됩니다.

Triple screenshot of the Circular Text page

텍스트 자체도 다소 원형으로 선택되었습니다. "원"이라는 단어는 문장의 주제이자 전치사 구의 개체입니다.