크기 조정 변환

Download Sample 샘플 다운로드

개체를 다양한 크기로 크기 조정하기 위한 SkiaSharp 확장 변환 살펴보기

변환 변환 문서에서 본 것처럼 변환 변환은 그래픽 개체를 한 위치에서 다른 위치로 이동할 수 있습니다. 반면, 배율 변환은 그래픽 개체의 크기를 변경합니다.

A tall word scaled in size

크기 조정 변환으로 인해 그래픽 좌표가 커지면서 이동하는 경우가 많습니다.

앞에서 변환 요소의 효과와 dy다음의 효과를 설명하는 두 가지 변환 수식을 보았습니다.dx

x' = x + dx

y' = y + dy

배율 인수 sxsy 가산보다는 곱합니다.

x' = sx · X

y' = sy · Y

변환 요소의 기본값은 0입니다. 배율 인수의 기본값은 1입니다.

클래스는 SKCanvasScale 가지 메서드를 정의합니다. 첫 번째 Scale 방법은 동일한 가로 및 세로 배율 인수를 원하는 경우를 위한 것입니다.

public void Scale (Single s)

이를 동위원소 배율이라고 하며 양방향에서 동일한 크기 조정입니다. 동위원소 배율 조정은 개체의 가로 세로 비율을 유지합니다.

두 번째 Scale 메서드를 사용하면 가로 및 세로 크기 조정에 대해 다른 값을 지정할 수 있습니다.

public void Scale (Single sx, Single sy)

이로 인해 이방성 크기 조정이 발생합니다. 세 번째 Scale 메서드는 두 배율 인수를 단일 SKPoint 값으로 결합합니다.

public void Scale (SKPoint size)

네 번째 Scale 메서드는 곧 설명됩니다.

기본 크기 조정 페이지에서 메서드를 보여 줍니다Scale. BasicScalePage.xaml 파일에는 0에서 10 사이의 가로 및 세로 배율 인수를 선택할 수 있는 두 가지 Slider 요소가 포함되어 있습니다. BasicScalePage.xaml.cs 코드 숨김 파일은 파선으로 스트로크되고 캔버스의 왼쪽 위 모서리에 있는 일부 텍스트에 맞게 크기가 조정된 둥근 사각형을 표시하기 전에 이러한 값을 사용하여 호출 Scale 합니다.

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

    canvas.Clear(SKColors.SkyBlue);

    using (SKPaint strokePaint = new SKPaint
    {
        Style = SKPaintStyle.Stroke,
        Color = SKColors.Red,
        StrokeWidth = 3,
        PathEffect = SKPathEffect.CreateDash(new float[] {  7, 7 }, 0)
    })
    using (SKPaint textPaint = new SKPaint
    {
        Style = SKPaintStyle.Fill,
        Color = SKColors.Blue,
        TextSize = 50
    })
    {
        canvas.Scale((float)xScaleSlider.Value,
                     (float)yScaleSlider.Value);

        SKRect textBounds = new SKRect();
        textPaint.MeasureText(Title, ref textBounds);

        float margin = 10;
        SKRect borderRect = SKRect.Create(new SKPoint(margin, margin), textBounds.Size);
        canvas.DrawRoundRect(borderRect, 20, 20, strokePaint);
        canvas.DrawText(Title, margin, -textBounds.Top + margin, textPaint);
    }
}

다음과 같이 궁금할 수 있습니다. 배율 인수가 메서드SKPaint에서 반환된 값에 MeasureText 어떤 영향을 주나요? 대답은 : 전혀. Scale 의 메서드 SKCanvas입니다. 해당 개체를 사용하여 캔버스에서 SKPaint 무언가를 렌더링하기 전까지는 개체로 수행하는 모든 작업이 영향을 주지 않습니다.

볼 수 있듯이 호출 후 그려진 모든 항목이 Scale 비례적으로 증가합니다.

Triple screenshot of the Basic Scale page

텍스트, 파선의 너비, 해당 줄의 대시 길이, 모서리의 반올림 및 캔버스의 왼쪽 가장자리와 위쪽 가장자리와 둥근 사각형 사이의 10픽셀 여백은 모두 동일한 배율 인수의 적용을 받습니다.

Important

유니버설 Windows 플랫폼 이방성 크기 조정 텍스트를 제대로 렌더링하지 않습니다.

이방성 배율을 사용하면 가로 축과 세로 축에 맞춰진 선의 스트로크 너비가 달라집니다. (이 페이지의 첫 번째 이미지에서도 알 수 있습니다.) 스트로크 너비가 배율 인수의 영향을 받지 않도록 하려면 0으로 설정하고 설정에 관계없이 Scale 항상 1픽셀 너비가 됩니다.

크기 조정은 캔버스의 왼쪽 위 모서리를 기준으로 합니다. 이것은 정확히 당신이 원하는 것 일 수 있지만, 그렇지 않을 수도 있습니다. 캔버스의 다른 위치에 텍스트와 직사각형을 배치하고 중심을 기준으로 크기를 조정하려는 경우를 가정해 보겠습니다. 이 경우 두 개의 추가 매개 변수를 포함하는 메서드의 Scale 네 번째 버전을 사용하여 크기 조정의 중심을 지정할 수 있습니다.

public void Scale (Single sx, Single sy, Single px, Single py)

py 매개 변수는 px 크기 조정 센터라고도 하지만 SkiaSharp 설명서에서 피벗 지점이라고도 하는 점을 정의합니다. 크기 조정의 영향을 받지 않는 캔버스의 왼쪽 위 모서리를 기준으로 하는 점입니다. 모든 크기 조정은 해당 중심을 기준으로 발생합니다.

가운데 맞춤 크기 조정 페이지에서 작동 방식을 보여 줍니다. PaintSurface 처리기는 기본 크기 조정 프로그램과 비슷하지만 값이 텍스트를 가로로 가운데에 맞게 계산된다는 점을 제외 margin 하면 프로그램이 세로 모드에서 가장 잘 작동한다는 것을 의미합니다.

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

    canvas.Clear(SKColors.SkyBlue);

    using (SKPaint strokePaint = new SKPaint
    {
        Style = SKPaintStyle.Stroke,
        Color = SKColors.Red,
        StrokeWidth = 3,
        PathEffect = SKPathEffect.CreateDash(new float[] { 7, 7 }, 0)
    })
    using (SKPaint textPaint = new SKPaint
    {
        Style = SKPaintStyle.Fill,
        Color = SKColors.Blue,
        TextSize = 50
    })
    {
        SKRect textBounds = new SKRect();
        textPaint.MeasureText(Title, ref textBounds);
        float margin = (info.Width - textBounds.Width) / 2;

        float sx = (float)xScaleSlider.Value;
        float sy = (float)yScaleSlider.Value;
        float px = margin + textBounds.Width / 2;
        float py = margin + textBounds.Height / 2;

        canvas.Scale(sx, sy, px, py);

        SKRect borderRect = SKRect.Create(new SKPoint(margin, margin), textBounds.Size);
        canvas.DrawRoundRect(borderRect, 20, 20, strokePaint);
        canvas.DrawText(Title, margin, -textBounds.Top + margin, textPaint);
    }
}

둥근 사각형의 왼쪽 위 모서리는 캔버스 margin 왼쪽에서 픽셀, 위쪽에서 픽셀로 배치 margin 됩니다. 메서드에 Scale 대한 마지막 두 인수는 해당 값과 텍스트의 너비 및 높이(둥근 사각형의 너비 및 높이)로 설정됩니다. 즉, 모든 크기 조정은 해당 사각형의 중심을 기준으로 합니다.

Triple screenshot of the Centered Scale page

Slider 이 프로그램의 요소는 –10~10의 범위를 갖습니다. 보듯이 세로 배율의 음수 값(예: 중앙의 Android 화면)으로 인해 개체가 배율 중심을 통과하는 가로 축을 뒤집습니다. 가로 배율의 음수 값(예: 오른쪽의 UWP 화면)으로 인해 개체가 배율 중심을 통과하는 세로 축을 뒤집습니다.

피벗 지점이 Scale 있는 메서드 버전은 일련의 3 Translate 개 및 Scale 호출에 대한 바로 가기입니다. 가운데 맞춤 크기 조정 페이지의 메서드Scale 다음으로 바꿔서 어떻게 작동하는지 확인할 수 있습니다.

canvas.Translate(-px, -py);

피벗 점 좌표의 음수입니다.

이제 프로그램을 다시 실행합니다. 가운데가 캔버스의 왼쪽 위 모서리에 있도록 사각형과 텍스트가 이동되는 것을 볼 수 있습니다. 당신은 거의 볼 수 없습니다. 슬라이더는 프로그램의 크기가 전혀 조정되지 않기 때문에 당연히 작동하지 않습니다.

이제 해당 호출 전에 기본 Scale 호출(크기 조정 센터 제외)Translate 추가합니다.

canvas.Scale(sx, sy);
canvas.Translate(–px, –py);

다른 그래픽 프로그래밍 시스템에서 이 연습을 잘 알고 있다면 틀렸을 수도 있지만 그렇지 않습니다. Skia는 익숙한 것과 약간 다르게 연속 변환 호출을 처리합니다.

연속 ScaleTranslate 호출을 사용하면 둥근 사각형의 중심이 여전히 왼쪽 위 모서리에 있지만, 이제 캔버스의 왼쪽 위 모서리를 기준으로 크기를 조정할 수 있습니다. 이는 둥근 사각형의 중심이기도 합니다.

이제 해당 Scale 호출 전에 가운데 맞춤 값을 사용하여 다른 Translate 호출을 추가합니다.

canvas.Translate(px, py);
canvas.Scale(sx, sy);
canvas.Translate(–px, –py);

이렇게 하면 크기가 조정된 결과가 원래 위치로 다시 이동합니다. 이러한 세 가지 호출은 다음과 같습니다.

canvas.Scale(sx, sy, px, py);

개별 변환은 총 변환 수식이 다음과 같이 되도록 복합화됩니다.

x' = sx · (x – px) + px

y' = sy · (y – py) + py

기본값 sx 은 1입니다 sy . 피벗 지점(px, py)이 이러한 수식에 의해 변환되지 않는다고 쉽게 확신할 수 있습니다. 캔버스를 기준으로 동일한 위치에 기본.

결합 Translate 하고 Scale 호출할 때 주문이 중요합니다. 이후의 TranslateScale경우 변환 요소는 배율 인수에 의해 효과적으로 크기 조정됩니다. Translate 앞에 오Scale면 변환 요소의 크기가 조정되지 않습니다. 이 프로세스는 변환 행렬의 주제가 도입될 때 다소 명확해집니다(수학적이기는 하지만).

이 클래스는 SKPath 경로에서 좌표의 익스텐트를 SKRect 정의하는 값을 반환하는 읽기 전용 Bounds 속성을 정의합니다. 예를 들어 이전에 Left 만든 헨데카그램 경로에서 속성을 가져온 경우 Bounds 사각형의 속성과 Top 속성은 약 –100이고 속성 RightBottom 약 100이고 Width 속성은 Height 약 200입니다. (별의 점이 반경이 100인 원에 의해 정의되지만 맨 위 점만 가로 또는 세로 축과 평행하기 때문에 실제 값의 대부분은 약간 적습니다.)

이 정보의 가용성은 캔버스 크기에 대한 경로 크기를 조정하는 데 적합한 배율을 파생시키고 인수를 변환할 수 있어야 한다는 것을 의미합니다. 이 방성 배율 페이지는 11개의 뾰족한 별을 사용하여 이를 보여줍니다. 이 방성 눈금은 가로 방향과 세로 방향이 같지 않음을 의미하며, 이는 별이 원래 가로 세로 비율을 유지하지 않음을 의미합니다. 처리기의 관련 코드는 PaintSurface 다음과 같습니다.

SKPath path = HendecagramPage.HendecagramPath;
SKRect pathBounds = path.Bounds;

using (SKPaint fillPaint = new SKPaint
{
    Style = SKPaintStyle.Fill,
    Color = SKColors.Pink
})
using (SKPaint strokePaint = new SKPaint
{
    Style = SKPaintStyle.Stroke,
    Color = SKColors.Blue,
    StrokeWidth = 3,
    StrokeJoin = SKStrokeJoin.Round
})
{
    canvas.Scale(info.Width / pathBounds.Width,
                 info.Height / pathBounds.Height);
    canvas.Translate(-pathBounds.Left, -pathBounds.Top);

    canvas.DrawPath(path, fillPaint);
    canvas.DrawPath(path, strokePaint);
}

사각형은 pathBounds 이 코드의 위쪽 근처에서 가져온 다음 나중에 호출 시 캔버스의 너비와 높이와 Scale 함께 사용됩니다. 이 호출은 호출에 의해 DrawPath 렌더링될 때 경로의 좌표 자체의 크기를 조정하지만 별은 캔버스의 오른쪽 위 모서리에 가운데에 배치됩니다. 아래로 왼쪽으로 이동해야 합니다. 이것은 호출의 작업입니다 Translate . 이러한 두 속성은 pathBounds 약 –100이므로 변환 요소는 약 100입니다. 호출은 Translate 호출 후 Scale 이므로 이러한 값은 배율 인수에 의해 효과적으로 크기 조정되므로 별의 중심을 캔버스의 중심으로 이동합니다.

Triple screenshot of the Anisotropic Scaling page

호출과 Translate 호출에 대해 Scale 생각할 수 있는 또 다른 방법은 역순으로 효과를 결정하는 것입니다. 호출은 Translate 경로를 이동하므로 완전히 표시되지만 캔버스의 왼쪽 위 모서리에서 방향을 지정합니다. 그런 다음 이 메서드는 Scale 왼쪽 위 모서리를 기준으로 해당 별을 더 크게 만듭니다.

실제로 별은 캔버스보다 조금 더 큰 것으로 보입니다. 문제는 스트로크 너비입니다. SKPath 속성은 Bounds 경로에 인코딩된 좌표의 차원을 나타내며, 이를 통해 프로그램의 크기를 조정합니다. 경로가 특정 스트로크 너비로 렌더링되면 렌더링된 경로가 캔버스보다 큽다.

이 문제를 해결하려면 이를 보완해야 합니다. 이 프로그램의 한 가지 쉬운 방법은 호출 바로 전에 다음 문을 추가하는 것입니다 Scale .

pathBounds.Inflate(strokePaint.StrokeWidth / 2,
                   strokePaint.StrokeWidth / 2);

이렇게 하면 pathBounds 4면에서 사각형이 1.5 단위 증가합니다. 이는 스트로크 조인이 반올림된 경우에만 적합한 솔루션입니다. 마이터 조인은 더 길 수 있으며 계산하기 어렵습니다.

이방성 텍스트 페이지에서 볼 수 있듯이 텍스트와 비슷한 기술을 사용할 수도 있습니다. 클래스의 처리기의 AnisotropicTextPage 관련 부분은 PaintSurface 다음과 같습니다.

using (SKPaint textPaint = new SKPaint
{
    Style = SKPaintStyle.Stroke,
    Color = SKColors.Blue,
    StrokeWidth = 0.1f,
    StrokeJoin = SKStrokeJoin.Round
})
{
    SKRect textBounds = new SKRect();
    textPaint.MeasureText("HELLO", ref textBounds);

    // Inflate bounds by the stroke width
    textBounds.Inflate(textPaint.StrokeWidth / 2,
                       textPaint.StrokeWidth / 2);

    canvas.Scale(info.Width / textBounds.Width,
                 info.Height / textBounds.Height);
    canvas.Translate(-textBounds.Left, -textBounds.Top);

    canvas.DrawText("HELLO", 0, 0, textPaint);
}

비슷한 논리이며 텍스트는 반환 MeasureText 된 텍스트 범위 사각형(실제 텍스트보다 약간 더 큰)에 따라 페이지 크기로 확장됩니다.

Triple screenshot of the Anisotropic Test page

그래픽 개체의 가로 세로 비율을 유지해야 하는 경우 동위원소 배율을 사용하는 것이 좋습니다. 동위원소 배율 페이지는 11개의 뾰족한 별에 대해 이를 보여 줍니다. 개념적으로, 동위원소 배율을 사용하여 페이지 중앙에 그래픽 개체를 표시하는 단계는 다음과 같습니다.

  • 그래픽 개체의 중심을 왼쪽 위 모서리로 변환합니다.
  • 그래픽 개체 차원으로 나눈 가로 및 세로 페이지 차원의 최소값에 따라 개체 크기를 조정합니다.
  • 크기 조정된 개체의 중심을 페이지 가운데로 변환합니다.

IsotropicScalingPage 을 표시하기 전에 다음 단계를 역순으로 수행합니다.

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

    canvas.Clear();

    SKPath path = HendecagramArrayPage.HendecagramPath;
    SKRect pathBounds = path.Bounds;

    using (SKPaint fillPaint = new SKPaint())
    {
        fillPaint.Style = SKPaintStyle.Fill;

        float scale = Math.Min(info.Width / pathBounds.Width,
                               info.Height / pathBounds.Height);

        for (int i = 0; i <= 10; i++)
        {
            fillPaint.Color = new SKColor((byte)(255 * (10 - i) / 10),
                                          0,
                                          (byte)(255 * i / 10));
            canvas.Save();
            canvas.Translate(info.Width / 2, info.Height / 2);
            canvas.Scale(scale);
            canvas.Translate(-pathBounds.MidX, -pathBounds.MidY);
            canvas.DrawPath(path, fillPaint);
            canvas.Restore();

            scale *= 0.9f;
        }
    }
}

또한 이 코드는 배율 인수를 10% 감소시키고 빨간색에서 파란색으로 점진적으로 변경할 때마다 별을 10번 더 표시합니다.

Triple screenshot of the Isotropic Scaling page