傾斜変換
傾斜変換によって SkiaSharp で傾いたグラフィカル オブジェクトを作成する方法を確認する
SkiaSharp では、傾斜変換によってグラフィカル オブジェクト (次の画像の影など) を傾けます。
傾斜によって四角形が平行四辺形に変化しますが、楕円は傾斜後も楕円のままです。
Xamarin.Forms には、移動、拡大縮小、回転のプロパティが定義されていますが、Xamarin.Forms に傾斜に対応するプロパティはありません。
SKCanvas
の Skew
メソッドは、水平方向の傾斜と垂直方向の傾斜を表す 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
呼び出しを明示的に結合する必要があります。 傾斜の中心を px
と py
に合わせるには、次の呼び出しを行います。
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
の値は使用されません。 値は無関係であり、ySkew
と py
も同様です。
この図の角度 α のように、傾きの角度として傾斜を指定する方が理解しやすい場合があります。
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));
}
SKPaint
の TextAlign
プロパティは 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" に置き換えると、次のようになります。
影とテキストは引き続きベースラインに沿って配置されていますが、間違った効果が適用されているように見えます。 これを修正するには、テキスト境界を取得する必要があります。
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);
これで、影はこれらのディセンダーの下部から伸びるようになります。