次の方法で共有


平行移動変換

移動変換を使用して SkiaSharp グラフィックをシフトする方法の詳細

SkiaSharp の中で最も簡単な変換の種類は、"移動" または "移動" 変換です。 この変換により、グラフィカル オブジェクトの向きが水平方向および垂直方向にシフトします。 通常は、描画関数で使用している座標を変更するだけで同じ効果を得られるため、ある意味で移動変換は最も不要な変換方法だと言うことができます。 しかし、パスをレンダリングするとすべての座標がパスにカプセル化されるため、移動変換を適用してパス全体をシフトする方がはるかに簡単です。

移動は、アニメーションや単純なテキスト効果にも役立ちます。

テキストの影、彫刻、浮き出し (変換付き)

TranslateSKCanvas メソッドには、以降に描画されたグラフィックス オブジェクトを水平方向および垂直方向にシフトさせる 2 つのパラメーターがあります。

public void Translate (Single dx, Single dy)

これらの引数は負の値になる場合があります。 2 番目の Translate メソッドでは、2 つの移動値を 1 つの 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);
        }
    }
}

四角形がページの下に向かって連なっています。

[累積変換] ページのトリプル スクリーンショット

累積された移動係数が dxdy、描画関数で指定する地点が (x, y) の場合、グラフィカル オブジェクトは地点 (x', y') にレンダリングされます。ここでは、次のようになります。

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

3 つの各例では、テキストを表示して、x および y 変数で指定した位置からオフセットするために Translate が呼び出されます。 次に、移動効果のない別の色でテキストが再び表示されます。

[テキスト効果の変換] ページのトリプル スクリーンショット

3 つの例ではそれぞれ、Translate 呼び出しを否定する別の方法を 示しています。

最初の例では、単に Translate をもう一度呼び出しますが、負の値を使用します。 Translate 呼び出しは累積的であるため、この一連の呼び出しでは、移動の合計が既定値の 0 に復元されます。

2 番目の例では、ResetMatrix が呼び出されます。 これにより、すべての変換が既定の状態に戻ります。

3 番目の例では、SKCanvas オブジェクトの状態が Save への呼び出しと共に保存され、次に Restore への呼び出しと共に状態が復元されます。 これは、一連の描画操作の変換を操作する最も汎用性の高い方法です。 Save および Restore 呼び出しは、スタックのように機能します。Save は複数回呼び出すことができ、次に Restore を逆順で呼び出して以前の状態に戻すことができます。 この Save メソッドは整数を返し、その整数を RestoreToCount に渡して Restore を効果的に複数回呼び出すことができます。 SaveCount プロパティは、スタックに現在保存されている状態の数を返します。

また、SKAutoCanvasRestore クラスを使用してキャンバスの状態を復元することもできます。 このクラスのコンストラクターは、using ステートメントで 呼び出されることを意図しています。キャンバスの状態は、using ブロックの最後で自動的に復元されます。

ただし、PaintSurface ハンドラーの呼び出しから次の呼び出しへの変換の引き継ぎについて心配する必要はありません。 PaintSurface への新しい呼び出しのたびに、既定の変換を含む新しい SKCanvas オブジェクトが提供されます。

Translate 変換のもう一つの一般的な用途は、描画に便利な座標を使用して最初に作成されたビジュアル オブジェクトをレンダリングすることです。 たとえば、地点 (0, 0) に中心を持つアナログ クロックの座標を指定するとします。 変換を使用して、必要な位置にクロックを表示できます。 この手法は、[HendecagramArray] ページで示されています。 HendecagramArrayPage クラスで、最初に十一芒星の 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 と増やしていくことで、十一芒星を作成することもできます)。円の半径は 100 に設定されます。

このパスが変換せずにレンダリングされる場合、中心はSKCanvas の左上隅に配置され、その 4 分の 1 のみが表示されます。 HendecagramPagePaintSurface ハンドラーでは、代わりに 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();
                }
        }
    }
}

結果は次のとおりです。

[Hendecagram Array] ページのトリプル スクリーンショット

多くの場合、アニメーションには変換が含まれます。 [Hendecagram Animation] ページでは、円の周囲で十一芒星が移動します。 HendecagramAnimationPage クラスは一部のフィールドで開始され、OnAppearing メソッドと OnDisappearing メソッドをオーバーライドして 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 プロパティを 2 つの方法で使用します。色合いを指定する SKColor.FromHsl メソッドと、星の位置を制御するために引数として使用する Math.Sin および Math.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 メソッドを 2 回呼び出します。1 回目でキャンバスの中心に移動し、2 回目で (0, 0) 周辺を中心とする円の円周に移動します。 円の半径は、ページの範囲内で星を維持しながら、できるだけ大きく設定されます。

[Hendecagram Animation] ページのトリプル スクリーンショット

星はページ中心の周囲を回転する際も同じ向きを維持します。 まったく回転しません。 これは回転変換の役割です。