Share via


傾斜変換

傾斜変換によって SkiaSharp で傾いたグラフィカル オブジェクトを作成する方法を確認する

SkiaSharp では、傾斜変換によってグラフィカル オブジェクト (次の画像の影など) を傾けます。

[傾斜影テキスト] プログラムからのスキューの例

傾斜によって四角形が平行四辺形に変化しますが、楕円は傾斜後も楕円のままです。

Xamarin.Forms には、移動、拡大縮小、回転のプロパティが定義されていますが、Xamarin.Forms に傾斜に対応するプロパティはありません。

SKCanvasSkew メソッドは、水平方向の傾斜と垂直方向の傾斜を表す 2 つの引数を受け取ります。

public void Skew (Single xSkew, Single ySkew)

2 つ目の Skew メソッドは、これらの引数を 1 つの SKPoint 値に結合します。

public void Skew (SKPoint skew)

ただし、これらの 2 つのメソッドのいずれかを単独で使用することはあまりありません。

[傾斜実験] ページでは、-10 から 10 までの傾斜値を試すことができます。 テキスト文字列はページの左上隅に配置され、2 つの Slider 要素から傾斜値が取得されます。 SkewExperimentPage クラスの PaintSurface ハンドラーを次に示します。

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
    {
        Style = SKPaintStyle.Fill,
        Color = SKColors.Blue,
        TextSize = 200
    })
    {
        string text = "SKEW";
        SKRect textBounds = new SKRect();
        textPaint.MeasureText(text, ref textBounds);

        canvas.Skew((float)xSkewSlider.Value, (float)ySkewSlider.Value);
        canvas.DrawText(text, 0, -textBounds.Top, textPaint);
    }
}

xSkew 引数の値は、正の値の場合はテキストの下部を右に、負の値の場合は左に移動します。 ySkew の値は、正の値の場合はテキストの右端を下に、負の値の場合は上に移動します。

[傾斜の実験] ページのトリプル スクリーンショット

xSkew の値が ySkew の値を負にした値になっている場合、結果として回転が行われますが、多少の拡大縮小も行われます。

変換の数式は次のとおりです。

x' = x + xSkew · y

y' = ySkew · x + y

たとえば、xSkew が正の場合、y が大きくなるにつれて変換された x' の値も大きくなります。 これが傾きの原因です。

幅 200 ピクセル、高さ 100 ピクセルの三角形の左上隅が点 (0, 0) に配置され、xSkew の値が 1.5 でレンダリングされると、次の平行四辺形が生成されます。

四角形に対する傾斜変換の効果

下端の座標の y の値は 100 であるため、右に 150 ピクセル移動します。

xSkew または ySkew が 0 以外の値の場合、点 (0, 0) のみが同じまま残ります。 この点を傾斜の中心と見なすことができます。 傾斜の中心を別の点にする必要がある場合 (通常の場合)、この操作を行う Skew メソッドはありません。 Translate 呼び出しと Skew 呼び出しを明示的に結合する必要があります。 傾斜の中心を pxpy に合わせるには、次の呼び出しを行います。

canvas.Translate(px, py);
canvas.Skew(xSkew, ySkew);
canvas.Translate(-px, -py);

複合変換の数式は次のとおりです。

x' = x + xSkew · (y – py)

y' = ySkew · (x – px) + y

ySkew が 0 の場合、px の値は使用されません。 値は無関係であり、ySkewpy も同様です。

この図の角度 α のように、傾きの角度として傾斜を指定する方が理解しやすい場合があります。

傾斜角度が示された四角形に対する傾斜変換の効果

150 ピクセルの移動に対して垂直方向に 100 ピクセル移動する場合の比率は、この角度の正接にあたり、この例では 56.3 度です。

[傾斜角度の実験] ページの XAML ファイルは [傾斜角度] ページに似ていますが、Slider 要素の範囲が -90 度から 90 度になっています。 SkewAngleExperiment の分離コード ファイルは、テキストをページの中央に配置し、Translate を使用して傾斜の中心をページの中央に設定します。 コードの下部にある短い SkewDegrees メソッドは、角度を傾斜値に変換します。

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
    {
        Style = SKPaintStyle.Fill,
        Color = SKColors.Blue,
        TextSize = 200
    })
    {
        float xCenter = info.Width / 2;
        float yCenter = info.Height / 2;

        string text = "SKEW";
        SKRect textBounds = new SKRect();
        textPaint.MeasureText(text, ref textBounds);
        float xText = xCenter - textBounds.MidX;
        float yText = yCenter - textBounds.MidY;

        canvas.Translate(xCenter, yCenter);
        SkewDegrees(canvas, xSkewSlider.Value, ySkewSlider.Value);
        canvas.Translate(-xCenter, -yCenter);
        canvas.DrawText(text, xText, yText, textPaint);
    }
}

void SkewDegrees(SKCanvas canvas, double xDegrees, double yDegrees)
{
    canvas.Skew((float)Math.Tan(Math.PI * xDegrees / 180),
                (float)Math.Tan(Math.PI * yDegrees / 180));
}

角度が正または負の 90 度に近づくと、正接は無限に近づきますが、最大約 80 度の角度まで使用できます。

[傾斜角の実験] ページのトリプル スクリーンショット

[斜投影テキスト] ページで示されているように、小さな負の水平方向の傾斜を使用すると、斜投影または斜体のテキストを模倣できます。 ObliqueTextPage クラスは、その実行方法を示しています。

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()
    {
        Style = SKPaintStyle.Fill,
        Color = SKColors.Maroon,
        TextAlign = SKTextAlign.Center,
        TextSize = info.Width / 8       // empirically determined
    })
    {
        canvas.Translate(info.Width / 2, info.Height / 2);
        SkewDegrees(canvas, -20, 0);
        canvas.DrawText(Title, 0, 0, textPaint);
    }
}

void SkewDegrees(SKCanvas canvas, double xDegrees, double yDegrees)
{
    canvas.Skew((float)Math.Tan(Math.PI * xDegrees / 180),
                (float)Math.Tan(Math.PI * yDegrees / 180));
}

SKPaintTextAlign プロパティは Center に設定されています。 変換を行わない場合、座標 (0, 0) で DrawText を呼び出すと、テキストはベースラインの水平方向の中心が左上隅になるように配置されます。 SkewDegrees は、テキストをベースラインを基準にして水平方向に 20 度傾斜させます。 Translate を呼び出すと、テキストのベースラインの水平方向の中心がキャンバスの中央に移動します。

[斜めテキスト] ページのトリプル スクリーンショット

[影付きテキストの傾斜] ページでは、45 度の傾斜と垂直方向の拡大縮小を結合し、テキストから離れる方向に傾くテキストの影を作成する方法を示します。 PaintSurface ハンドラーの関連部分を次に示します。

using (SKPaint textPaint = new SKPaint())
{
    textPaint.Style = SKPaintStyle.Fill;
    textPaint.TextSize = info.Width / 6;   // empirically determined

    // Common to shadow and text
    string text = "Shadow";
    float xText = 20;
    float yText = info.Height / 2;

    // Shadow
    textPaint.Color = SKColors.LightGray;
    canvas.Save();
    canvas.Translate(xText, yText);
    canvas.Skew((float)Math.Tan(-Math.PI / 4), 0);
    canvas.Scale(1, 3);
    canvas.Translate(-xText, -yText);
    canvas.DrawText(text, xText, yText, textPaint);
    canvas.Restore();

    // Text
    textPaint.Color = SKColors.Blue;
    canvas.DrawText(text, xText, yText, textPaint);
}

最初に影が表示され、次にテキストが表示されます。

[傾斜影テキスト] ページのトリプル スクリーンショット

DrawText メソッドに渡される垂直座標は、ベースラインを基準としたテキストの位置を示しています。 これは、傾斜の中心に使用されるのと同じ垂直座標です。 テキスト文字列にディセンダーが含まれている場合、この手法は機能しません。 たとえば、"Shadow" を "quirky" に置き換えると、次のようになります。

[傾斜影テキスト] ページの 3 つのスクリーンショット(ぶら下がり部分のある代替単語)

影とテキストは引き続きベースラインに沿って配置されていますが、間違った効果が適用されているように見えます。 これを修正するには、テキスト境界を取得する必要があります。

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

Translate 呼び出しをディセンダーの高さによって調整する必要があります。

canvas.Translate(xText, yText + textBounds.Bottom);
canvas.Skew((float)Math.Tan(-Math.PI / 4), 0);
canvas.Scale(1, 3);
canvas.Translate(-xText, -yText - textBounds.Bottom);

これで、影はこれらのディセンダーの下部から伸びるようになります。

ぶら下がり部分の調整を含む [傾斜影テキスト] ページのトリプル スクリーンショット