다음을 통해 공유


좌표 이동 변환

변환 변환을 사용하여 SkiaSharp 그래픽을 이동하는 방법 알아보기

SkiaSharp에서 가장 간단한 변환 유형은 번역 또는 번역 변환입니다. 이 변환은 그래픽 개체를 가로 및 세로 방향으로 이동합니다. 어떤 의미에서 변환은 그리기 함수에서 사용하는 좌표를 단순히 변경하여 동일한 효과를 달성할 수 있기 때문에 가장 불필요한 변환입니다. 그러나 경로를 렌더링할 때는 모든 좌표가 경로에 캡슐화되므로 변환 변환을 적용하여 전체 경로를 이동하는 것이 훨씬 쉽습니다.

번역은 애니메이션 및 간단한 텍스트 효과에도 유용합니다.

텍스트 그림자, 조각화 및 변환으로 구현

메서드에는 TranslateSKCanvas 이후에 그려진 그래픽 개체가 가로 및 세로로 이동되도록 하는 두 개의 매개 변수가 있습니다.

public void Translate (Single dx, Single dy)

이러한 인수는 음수일 수 있습니다. 두 번째 Translate 메서드는 두 변환 값을 단일 SKPoint 값으로 결합합니다.

public void Translate (SKPoint point)

샘플 프로그램의 누적 번역 페이지는 메서드의 여러 호출이 누적됨을 Translate 보여 줍니다. 클래스는 AccumulatedTranslatePage 동일한 사각형의 20개 버전을 표시하며, 각 버전은 대각선을 따라 뻗어 있을 정도로 이전 사각형의 오프셋입니다. PaintSurface 이벤트 처리기는 다음과 같습니다.

void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
    SKImageInfo info = args.Info;
    SKSurface surface = args.Surface;
    SKCanvas canvas = surface.Canvas;

    canvas.Clear();

    using (SKPaint strokePaint = new SKPaint())
    {
        strokePaint.Color = SKColors.Black;
        strokePaint.Style = SKPaintStyle.Stroke;
        strokePaint.StrokeWidth = 3;

        int rectangleCount = 20;
        SKRect rect = new SKRect(0, 0, 250, 250);
        float xTranslate = (info.Width - rect.Width) / (rectangleCount - 1);
        float yTranslate = (info.Height - rect.Height) / (rectangleCount - 1);

        for (int i = 0; i < rectangleCount; i++)
        {
            canvas.DrawRect(rect, strokePaint);
            canvas.Translate(xTranslate, yTranslate);
        }
    }
}

연속 사각형이 페이지 아래로 흘러내립니다.

누적 번역 페이지의 삼중 스크린샷

누적된 변환 요소와 dy그리기 함수에서 지정한 점이 (x, y),이면 그래픽 개체가 지점(x', y')에서 렌더링됩니다. 여기서 다음을 수행합니다dx.

x' = x + dx

y' = y + dy

이를 번역을 위한 변환 수식이라고 합니다 . 새 SKCanvas 항목의 dx 기본값은 dy 0입니다.

텍스트 효과 번역 페이지에서 볼 수 있듯이 그림자 효과 및 유사한 기술에 번역 변환을 사용하는 것이 일반적입니다. 클래스의 처리기의 TranslateTextEffectsPage 관련 부분은 PaintSurface 다음과 같습니다.

float textSize = 150;

using (SKPaint textPaint = new SKPaint())
{
    textPaint.Style = SKPaintStyle.Fill;
    textPaint.TextSize = textSize;
    textPaint.FakeBoldText = true;

    float x = 10;
    float y = textSize;

    // Shadow
    canvas.Translate(10, 10);
    textPaint.Color = SKColors.Black;
    canvas.DrawText("SHADOW", x, y, textPaint);
    canvas.Translate(-10, -10);
    textPaint.Color = SKColors.Pink;
    canvas.DrawText("SHADOW", x, y, textPaint);

    y += 2 * textSize;

    // Engrave
    canvas.Translate(-5, -5);
    textPaint.Color = SKColors.Black;
    canvas.DrawText("ENGRAVE", x, y, textPaint);
    canvas.ResetMatrix();
    textPaint.Color = SKColors.White;
    canvas.DrawText("ENGRAVE", x, y, textPaint);

    y += 2 * textSize;

    // Emboss
    canvas.Save();
    canvas.Translate(5, 5);
    textPaint.Color = SKColors.Black;
    canvas.DrawText("EMBOSS", x, y, textPaint);
    canvas.Restore();
    textPaint.Color = SKColors.White;
    canvas.DrawText("EMBOSS", x, y, textPaint);
}

세 가지 Translate 예제 각각에서 지정한 위치 xy 변수에서 오프셋하는 텍스트를 표시하기 위해 호출됩니다. 그런 다음 번역 효과가 없는 다른 색으로 텍스트가 다시 표시됩니다.

텍스트 효과 번역 페이지의 세 가지 스크린샷

세 가지 예제 각각은 호출을 부정하는 Translate 다른 방법을 보여 줍니다.

첫 번째 예제에서는 단순히 다시 호출 Translate 하지만 음수 값을 사용합니다. 호출은 Translate 누적되므로 이 호출 시퀀스는 단순히 전체 변환을 기본값 0으로 복원합니다.

두 번째 예제에서는 .를 호출합니다 ResetMatrix. 이렇게 하면 모든 변환이 기본 상태로 돌아갑니다.

세 번째 예제에서는 호출 Save 을 사용하여 개체의 SKCanvas 상태를 저장한 다음 호출을 사용하여 Restore상태를 복원합니다. 이 방법은 일련의 그리기 작업에 대한 변환을 조작하는 가장 다양한 방법입니다. 이러한 Save 호출은 스택과 Restore 같은 함수입니다. 여러 번 호출 Save 한 다음 역순으로 호출 Restore 하여 이전 상태로 돌아갈 수 있습니다. 이 메서드는 Save 정수를 반환하며, 이 정수는 여러 번 효과적으로 호출 Restore 하도록 RestoreToCount 전달할 수 있습니다. 이 속성은 SaveCount 현재 스택에 저장된 상태 수를 반환합니다.

캔버스 상태를 복원하는 데 클래스를 사용할 SKAutoCanvasRestore 수도 있습니다. 이 클래스의 생성자는 문에서 using 호출됩니다. 캔버스 상태는 블록의 using 끝에서 자동으로 복원됩니다.

그러나 처리기의 한 호출에서 다음 호출로 전달되는 변환에 PaintSurface 대해 걱정할 필요가 없습니다. 각 새 호출은 PaintSurface 기본 변환을 사용하여 새 SKCanvas 개체를 제공합니다.

변환의 Translate 또 다른 일반적인 용도는 그리기에 편리한 좌표를 사용하여 원래 만들어진 시각적 개체를 렌더링하는 것입니다. 예를 들어 중심이 점(0, 0)인 아날로그 클록의 좌표를 지정할 수 있습니다. 그런 다음 변환을 사용하여 원하는 위치에 시계를 표시할 수 있습니다. 이 기술은 [헨데카그램 배열] 페이지에서 설명합니다. 클래스는 HendecagramArrayPage 11개의 뾰족한 별에 대한 개체를 만들어 SKPath 시작합니다. 개체는 HendecagramPath 공용, 정적 및 읽기 전용으로 정의되므로 다른 데모 프로그램에서 액세스할 수 있습니다. 정적 생성자에서 생성됩니다.

public class HendecagramArrayPage : ContentPage
{
    ...
    public static readonly SKPath HendecagramPath;

    static HendecagramArrayPage()
    {
        // Create 11-pointed star
        HendecagramPath = new SKPath();
        for (int i = 0; i < 11; i++)
        {
            double angle = 5 * i * 2 * Math.PI / 11;
            SKPoint pt = new SKPoint(100 * (float)Math.Sin(angle),
                                    -100 * (float)Math.Cos(angle));
            if (i == 0)
            {
                HendecagramPath.MoveTo(pt);
            }
            else
            {
                HendecagramPath.LineTo(pt);
            }
        }
        HendecagramPath.Close();
    }
}

별의 중심이 점(0, 0)이면 별의 모든 점이 해당 지점을 둘러싼 원에 있습니다. 각 지점은 360도의 5/11까지 증가하는 각도의 사인 값과 코사인 값의 조합입니다. (원의 각도를 2/11, 3/11 또는 4/11까지 늘려 11개의 뾰족한 별을 만들 수도 있습니다.) 해당 원의 반지름은 100으로 설정됩니다.

이 경로가 변환 없이 렌더링되는 경우 가운데는 왼쪽 위 모서리 SKCanvas에 배치되고 그 중 4분의 1만 표시됩니다. PaintSurface 대신 처리기는 HendecagramPage 별의 여러 복사본을 사용하여 캔버스에 타일을 지정하는 데 사용합니다Translate. 각 개체는 임의로 색이 지정됩니다.

public class HendecagramArrayPage : ContentPage
{
    Random random = new Random();
    ...
    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();

        using (SKPaint paint = new SKPaint())
        {
            for (int x = 100; x < info.Width + 100; x += 200)
                for (int y = 100; y < info.Height + 100; y += 200)
                {
                    // Set random color
                    byte[] bytes = new byte[3];
                    random.NextBytes(bytes);
                    paint.Color = new SKColor(bytes[0], bytes[1], bytes[2]);

                    // Display the hendecagram
                    canvas.Save();
                    canvas.Translate(x, y);
                    canvas.DrawPath(HendecagramPath, paint);
                    canvas.Restore();
                }
        }
    }
}

결과:

헨데카그램 배열 페이지의 삼중 스크린샷

애니메이션에는 종종 변환이 포함됩니다. 헨데카그램 애니메이션 페이지는 원을 그리며 11-뾰족한 별을 이동합니다. 클래스는 HendecagramAnimationPage 타이머를 시작하고 중지하기 위한 일부 필드와 OnDisappearing 메서드의 재정의 OnAppearing 로 Xamarin.Forms 시작합니다.

public class HendecagramAnimationPage : ContentPage
{
    const double cycleTime = 5000;      // in milliseconds

    SKCanvasView canvasView;
    Stopwatch stopwatch = new Stopwatch();
    bool pageIsActive;
    float angle;

    public HendecagramAnimationPage()
    {
        Title = "Hedecagram Animation";

        canvasView = new SKCanvasView();
        canvasView.PaintSurface += OnCanvasViewPaintSurface;
        Content = canvasView;
    }

    protected override void OnAppearing()
    {
        base.OnAppearing();
        pageIsActive = true;
        stopwatch.Start();

        Device.StartTimer(TimeSpan.FromMilliseconds(33), () =>
        {
            double t = stopwatch.Elapsed.TotalMilliseconds % cycleTime / cycleTime;
            angle = (float)(360 * t);
            canvasView.InvalidateSurface();

            if (!pageIsActive)
            {
                stopwatch.Stop();
            }

            return pageIsActive;
        });
    }

    protected override void OnDisappearing()
    {
        base.OnDisappearing();
        pageIsActive = false;
    }
    ...
}

angle 필드는 5초마다 0도에서 360도로 애니메이션됩니다. PaintSurface 처리기는 두 가지 방법으로 속성을 사용 합니다angle. 메서드에서 SKColor.FromHsl 색의 색조를 지정 하 고 별의 위치를 제어 하는 Math.SinMath.Cos 및 메서드에 대 한 인수로:

public class HendecagramAnimationPage : ContentPage
{
    ...
    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();
        canvas.Translate(info.Width / 2, info.Height / 2);
        float radius = (float)Math.Min(info.Width, info.Height) / 2 - 100;

        using (SKPaint paint = new SKPaint())
        {
            paint.Style = SKPaintStyle.Fill;
            paint.Color = SKColor.FromHsl(angle, 100, 50);

            float x = radius * (float)Math.Sin(Math.PI * angle / 180);
            float y = -radius * (float)Math.Cos(Math.PI * angle / 180);
            canvas.Translate(x, y);
            canvas.DrawPath(HendecagramPage.HendecagramPath, paint);
        }
    }
}

PaintSurface 처리기는 메서드를 Translate 두 번 호출하고, 먼저 캔버스의 중심으로 변환한 다음, 가운데에 있는 원의 둘레(0, 0)로 변환합니다. 원의 반지름은 페이지의 범위 내에서 별을 유지하면서 가능한 한 크게 설정됩니다.

헨데카그램 애니메이션 페이지의 삼중 스크린샷

별은 기본 페이지의 가운데를 중심으로 회전할 때와 동일한 방향을 지정합니다. 전혀 회전하지 않습니다. 회전 변환에 대한 작업입니다.