線とストローク キャップ
SkiaSharp を使用して、さまざまなストローク キャップで線を描画する方法の説明
SkiaSharp において、1 本の線のレンダリングは、一連の接続された複数の直線のレンダリングとは大きく異なります。 ただし、1 本の線を描画する場合でも、多くの場合、その線に特定のストローク幅を指定する必要があります。 これらの線が広くなるにつれて、線の端の外観も重要になります。 線の端の外観は、"ストローク キャップ" と呼ばれます。
1 本の線を描画する場合、SKCanvas
では単純な DrawLine
メソッド (引数は線の始点および終点の座標と、 SKPaint
オブジェクトを示します) を定義します。
canvas.DrawLine (x0, y0, x1, y1, paint);
既定では、新しくインスタンス化された SKPaint
オブジェクトの StrokeWidth
プロパティは 0 で、これは太さ 1 ピクセルの線をレンダリングする場合の値 1 と同じ効果があります。 これは携帯電話などの高解像度デバイス上では非常に細く表示されるため、StrokeWidth
をより大きな値に設定することをお勧めします。 しかし、相当な太さの線を描画し始めると、これらの太い線の始点と終点をどうレンダリングするべきかという、別の問題が生じます。
線の始点や終点の外観は "ライン キャップ"、または Skia では "ストローク キャップ" と呼ばれます。 このコンテキストの中での "キャップ" という単語は、ある種の帽子 (線の端にあるもの) のことを指します。 SKPaint
オブジェクトの StrokeCap
プロパティに、SKStrokeCap
列挙型の次のメンバーのいずれかを設定します。
Butt
(既定値)Square
Round
これらは、サンプル プログラムで最もよく説明されています。 サンプル プログラムの [SkiaSharp の線とパス] セクションは、StrokeCapsPage
クラスをベースにした [ストローク キャップ] というタイトルのページから始まります。 このページでは、SKStrokeCap
列挙型の 3 つのメンバーをループ処理する PaintSurface
イベント ハンドラーを定義し、列挙型メンバーの名前と、そのストローク キャップを使用した線の描画の両方を表示します。
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
SKPaint textPaint = new SKPaint
{
Color = SKColors.Black,
TextSize = 75,
TextAlign = SKTextAlign.Center
};
SKPaint thickLinePaint = new SKPaint
{
Style = SKPaintStyle.Stroke,
Color = SKColors.Orange,
StrokeWidth = 50
};
SKPaint thinLinePaint = new SKPaint
{
Style = SKPaintStyle.Stroke,
Color = SKColors.Black,
StrokeWidth = 2
};
float xText = info.Width / 2;
float xLine1 = 100;
float xLine2 = info.Width - xLine1;
float y = textPaint.FontSpacing;
foreach (SKStrokeCap strokeCap in Enum.GetValues(typeof(SKStrokeCap)))
{
// Display text
canvas.DrawText(strokeCap.ToString(), xText, y, textPaint);
y += textPaint.FontSpacing;
// Display thick line
thickLinePaint.StrokeCap = strokeCap;
canvas.DrawLine(xLine1, y, xLine2, y, thickLinePaint);
// Display thin line
canvas.DrawLine(xLine1, y, xLine2, y, thinLinePaint);
y += 2 * textPaint.FontSpacing;
}
}
SKStrokeCap
列挙型の各メンバーについて、このハンドラーは 2 本の線を描画します。1 本はストロークの太さが 50 ピクセル、もう 1 本はストロークの太さが 2 ピクセルで上部に配置されます。 この 2 本目の線は、線の太さとストローク キャップに関係なく、線の幾何学的な始点と終点を示すことを目的としています。
ご覧のように、Square
と Round
のストローク キャップは線の始点と、また終点でも、ストローク幅の半分だけ線の長さを効果的に延長しています。 この延長は、レンダリングされたグラフィックス オブジェクトの寸法を決定する必要がある場合に重要になります。
SKCanvas
クラスには、やや独特な複数の線を描画するための別のメソッドも含まれています。
DrawPoints (SKPointMode mode, points, paint)
points
パラメーターは SKPoint
値の配列です。mode
は SKPointMode
列挙型のメンバーであり、次の 3 つのメンバーがあります。
Points
: 個々のポイントをレンダリングしますLines
: ポイントの各ペアを接続しますPolygon
: すべての連続するポイントを接続します
Multiple Lines Page ではこのメソッドを示します。 MultipleLinesPage.xaml ファイルは、SKPointMode
列挙型のメンバーと SKStrokeCap
列挙型のメンバーを選択できる 2 つの Picker
ビューをインスタンス化します。
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:skia="clr-namespace:SkiaSharp;assembly=SkiaSharp"
xmlns:skiaforms="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
x:Class="SkiaSharpFormsDemos.Paths.MultipleLinesPage"
Title="Multiple Lines">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Picker x:Name="pointModePicker"
Title="Point Mode"
Grid.Row="0"
Grid.Column="0"
SelectedIndexChanged="OnPickerSelectedIndexChanged">
<Picker.ItemsSource>
<x:Array Type="{x:Type skia:SKPointMode}">
<x:Static Member="skia:SKPointMode.Points" />
<x:Static Member="skia:SKPointMode.Lines" />
<x:Static Member="skia:SKPointMode.Polygon" />
</x:Array>
</Picker.ItemsSource>
<Picker.SelectedIndex>
0
</Picker.SelectedIndex>
</Picker>
<Picker x:Name="strokeCapPicker"
Title="Stroke Cap"
Grid.Row="0"
Grid.Column="1"
SelectedIndexChanged="OnPickerSelectedIndexChanged">
<Picker.ItemsSource>
<x:Array Type="{x:Type skia:SKStrokeCap}">
<x:Static Member="skia:SKStrokeCap.Butt" />
<x:Static Member="skia:SKStrokeCap.Round" />
<x:Static Member="skia:SKStrokeCap.Square" />
</x:Array>
</Picker.ItemsSource>
<Picker.SelectedIndex>
0
</Picker.SelectedIndex>
</Picker>
<skiaforms:SKCanvasView x:Name="canvasView"
PaintSurface="OnCanvasViewPaintSurface"
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2" />
</Grid>
</ContentPage>
SkiaSharp
名前空間は SKPointMode
および SKStrokeCap
列挙型のメンバーを参照するために必要であるため、SkiaSharp 名前空間の宣言は少し異なっていることに注意してください。 両方の Picker
ビューの SelectedIndexChanged
ハンドラーは、単に SKCanvasView
オブジェクトを無効にします。
void OnPickerSelectedIndexChanged(object sender, EventArgs args)
{
if (canvasView != null)
{
canvasView.InvalidateSurface();
}
}
このハンドラーは SKCanvasView
オブジェクトの存在をチェックする必要があります。それは、XAML ファイル内で Picker
の SelectedIndex
プロパティに 0 が設定される際にこのイベント ハンドラーが最初に呼び出され、かつ SKCanvasView
がインスタンス化される前にそれが実行されるためです。
PaintSurface
ハンドラーは、Picker
ビューから 2 つの列挙値を取得します。
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
// Create an array of points scattered through the page
SKPoint[] points = new SKPoint[10];
for (int i = 0; i < 2; i++)
{
float x = (0.1f + 0.8f * i) * info.Width;
for (int j = 0; j < 5; j++)
{
float y = (0.1f + 0.2f * j) * info.Height;
points[2 * j + i] = new SKPoint(x, y);
}
}
SKPaint paint = new SKPaint
{
Style = SKPaintStyle.Stroke,
Color = SKColors.DarkOrchid,
StrokeWidth = 50,
StrokeCap = (SKStrokeCap)strokeCapPicker.SelectedItem
};
// Render the points by calling DrawPoints
SKPointMode pointMode = (SKPointMode)pointModePicker.SelectedItem;
canvas.DrawPoints(pointMode, points, paint);
}
このスクリーンショットにはさまざまな Picker
の選択項目が示されています。
左側の iPhone は、ライン キャップが Butt
または Square
の場合に、SKPointMode.Points
列挙型メンバーによって DrawPoints
が SKPoint
配列内の各ポイントを正方形としてどのようにレンダリングするかを示しています。 ライン キャップが Round
の場合は円がレンダリングされます。
Android のスクリーンショットは、SKPointMode.Lines
の結果を示しています。 DrawPoints
メソッドでは、指定されたライン キャップ (この場合は Round
) を使用して、SKPoint
値の各ペアの間に線を描画します。
代わりに SKPointMode.Polygon
を使用すると、配列内の連続するポイントの間に線が描画されます。しかし、よく見るとこれらの線が接続されていないことがわかります。 これらの個別の線はそれぞれ、始点と終点に指定したライン キャップが使用されます。 Round
キャップを選択すると、その線は接続されているように見えますが、実際には接続されていません。
線が接続されているかどうかは、グラフィックス パスを操作する上できわめて重要な側面です。