次の方法で共有


パスの塗りつぶしの種類

SkiaSharp パスの各塗りつぶしタイプで可能なさまざまな効果を検出する

パスの中の2つの輪郭が重なることもあれば、1つの輪郭を構成する線が重なることもあります。 囲まれた領域は塗りつぶすことができますが、囲まれた領域をすべて塗りつぶしたくない場合もあるでしょう。 次に例を示します。

部分的に塗りつぶされた五芒星

これに関してコントロールできることは限られています。 塗りつぶしアルゴリズムは、SKPathFillType 列挙型のメンバーに設定した SKPathSKFillType プロパティによって制御されます。

  • Winding (既定値)
  • EvenOdd
  • InverseWinding
  • InverseEvenOdd

巻き取りアルゴリズムと偶数奇数アルゴリズムの両方が、その領域から無限大まで引かれた仮定の線に基づいて、囲まれた領域が塗りつぶされるか、または塗りつぶされないかを決定します。 その線は、パスを構成する 1 つ以上の境界線を横切ります。 巻き取りモードでは、一方向に引かれた境界線の本数と、他方向に引かれた境界線の本数が釣り合っている場合、その領域は塗りつぶされません。 それ以外の場合は、その領域は塗りつぶされます。 境界線の数が奇数の場合、偶数奇数アルゴリズムによりその領域が塗りつぶされます。

多くのルーチン パスでは、多くの場合、巻き取りアルゴリズムによりパスの囲まれたすべての領域が塗りつぶされます。 偶数奇数アルゴリズムは、一般に、より興味深い結果を生成します。

典型的な例は、「五芒星」のページで示されているように、五芒星です。 FivePointedStarPage.xaml ファイルは、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.FivePointedStarPage"
             Title="Five-Pointed Star">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

        <Picker x:Name="fillTypePicker"
                Title="Path Fill Type"
                Grid.Row="0"
                Grid.Column="0"
                Margin="10"
                SelectedIndexChanged="OnPickerSelectedIndexChanged">
            <Picker.ItemsSource>
                <x:Array Type="{x:Type skia:SKPathFillType}">
                    <x:Static Member="skia:SKPathFillType.Winding" />
                    <x:Static Member="skia:SKPathFillType.EvenOdd" />
                    <x:Static Member="skia:SKPathFillType.InverseWinding" />
                    <x:Static Member="skia:SKPathFillType.InverseEvenOdd" />
                </x:Array>
            </Picker.ItemsSource>
            <Picker.SelectedIndex>
                0
            </Picker.SelectedIndex>
        </Picker>

        <Picker x:Name="drawingModePicker"
                Title="Drawing Mode"
                Grid.Row="0"
                Grid.Column="1"
                Margin="10"
                SelectedIndexChanged="OnPickerSelectedIndexChanged">
            <Picker.ItemsSource>
                <x:Array Type="{x:Type x:String}">
                    <x:String>Fill only</x:String>
                    <x:String>Stroke only</x:String>
                    <x:String>Stroke then Fill</x:String>
                    <x:String>Fill then Stroke</x:String>
                </x:Array>
            </Picker.ItemsSource>
            <Picker.SelectedIndex>
                0
            </Picker.SelectedIndex>
        </Picker>

        <skiaforms:SKCanvasView x:Name="canvasView"
                                Grid.Row="1"
                                Grid.Column="0"
                                Grid.ColumnSpan="2"
                                PaintSurface="OnCanvasViewPaintSurface" />
    </Grid>
</ContentPage>

分離コード ファイルでは、両方の Picker 値を使用して、五芒星を描画します。

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

    canvas.Clear();

    SKPoint center = new SKPoint(info.Width / 2, info.Height / 2);
    float radius = 0.45f * Math.Min(info.Width, info.Height);

    SKPath path = new SKPath
    {
        FillType = (SKPathFillType)fillTypePicker.SelectedItem
    };
    path.MoveTo(info.Width / 2, info.Height / 2 - radius);

    for (int i = 1; i < 5; i++)
    {
        // angle from vertical
        double angle = i * 4 * Math.PI / 5;
        path.LineTo(center + new SKPoint(radius * (float)Math.Sin(angle),
                                        -radius * (float)Math.Cos(angle)));
    }
    path.Close();

    SKPaint strokePaint = new SKPaint
    {
        Style = SKPaintStyle.Stroke,
        Color = SKColors.Red,
        StrokeWidth = 50,
        StrokeJoin = SKStrokeJoin.Round
    };

    SKPaint fillPaint = new SKPaint
    {
        Style = SKPaintStyle.Fill,
        Color = SKColors.Blue
    };

    switch ((string)drawingModePicker.SelectedItem)
    {
        case "Fill only":
            canvas.DrawPath(path, fillPaint);
            break;

        case "Stroke only":
            canvas.DrawPath(path, strokePaint);
            break;

        case "Stroke then Fill":
            canvas.DrawPath(path, strokePaint);
            canvas.DrawPath(path, fillPaint);
            break;

        case "Fill then Stroke":
            canvas.DrawPath(path, fillPaint);
            canvas.DrawPath(path, strokePaint);
            break;
    }
}

通常、パスの塗りつぶしの種類はストロークではなく塗りつぶしにのみ影響しますが、2 つの Inverse モードでは塗りつぶしとストロークの両方に影響します。 塗りつぶしの場合、2 つの Inverse 型は、星の外側の領域が塗りつぶされるように、反対方向に領域を塗りつぶします。 ストロークの場合、2 つの Inverse 型はストローク以外のすべてを色付けします。 iOS のスクリーンショットが示すように、これらの逆塗りつぶしタイプを使用すると、いくつかの変わった効果が得られます。

[五芒星] ページのトリプル スクリーンショット

Android のスクリーンショットは、一般的な偶数奇数効果と巻き取り効果を示していますが、ストロークと塗りつぶしの順序も結果に影響します。

巻き取りアルゴリズムは、線が引かれる方向に依存します。 通常、パスを作成するときに、ある点から別の点へ線を引くように指定することで、その方向をコントロールできます。 ただし、SKPath クラスでは、輪郭全体を描画する AddRectAddCircle などのメソッドも定義されます。 これらのオブジェクトの描画方法をコントロールするために、これらのメソッドには 2 つのメンバーを持つ SKPathDirection 型のパラメーターが含まれています。

  • Clockwise
  • CounterClockwise

SKPath のメソッドに SKPathDirection パラメータが含まれている場合、既定値は Clockwise になります。

[重なり合う円] ページでは、偶数奇数パスの塗りつぶしタイプを使用して、4つの円が重なり合うパスを作成します。

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

    canvas.Clear();

    SKPoint center = new SKPoint(info.Width / 2, info.Height / 2);
    float radius = Math.Min(info.Width, info.Height) / 4;

    SKPath path = new SKPath
    {
        FillType = SKPathFillType.EvenOdd
    };

    path.AddCircle(center.X - radius / 2, center.Y - radius / 2, radius);
    path.AddCircle(center.X - radius / 2, center.Y + radius / 2, radius);
    path.AddCircle(center.X + radius / 2, center.Y - radius / 2, radius);
    path.AddCircle(center.X + radius / 2, center.Y + radius / 2, radius);

    SKPaint paint = new SKPaint()
    {
        Style = SKPaintStyle.Fill,
        Color = SKColors.Cyan
    };

    canvas.DrawPath(path, paint);

    paint.Style = SKPaintStyle.Stroke;
    paint.StrokeWidth = 10;
    paint.Color = SKColors.Magenta;

    canvas.DrawPath(path, paint);
}

これは、最小限のコードで作成された興味深いイメージです。

[重なり合う円] ページのトリプル スクリーンショット