Freigeben über


Die Fülltypen für Pfade

Entdecken Sie die verschiedenen Effekte, die mit SkiaSharp-Pfadfülltypen möglich sind

Zwei Konturen in einem Pfad können überlappen, und die Linien, aus denen eine einzelne Kontur besteht, können sich überlappen. Jeder eingeschlossene Bereich kann möglicherweise ausgefüllt werden, aber Sie möchten möglicherweise nicht alle eingeschlossenen Bereiche ausfüllen. Ein Beispiel:

Fünfzackige Sternfüllungen

Sie haben ein wenig Kontrolle darüber. Der Füllalgorithmus wird durch die SKFillType Eigenschaft bestimmt, von SKPathder Sie auf ein Element der SKPathFillType Enumeration festlegen:

  • Winding, der Standardwert.
  • EvenOdd
  • InverseWinding
  • InverseEvenOdd

Sowohl die Wicklungsalgorithmen als auch die geraden Algorithmen bestimmen, ob ein eingeschlossener Bereich gefüllt oder nicht gefüllt ist, basierend auf einer hypothetischen Linie, die von diesem Bereich bis zur Unendlichkeit gezeichnet wird. Diese Linie überschreitet eine oder mehrere Begrenzungslinien, die den Pfad bilden. Wenn im Wicklungsmodus die Anzahl von Begrenzungslinien, die in eine Richtung gezeichnet wurden, die Anzahl der in die andere Richtung gezeichneten Linien ausgeglichen wird, wird der Bereich nicht gefüllt. Andernfalls wird der Bereich ausgefüllt. Der gerade ungerade Algorithmus füllt einen Bereich aus, wenn die Anzahl der Begrenzungslinien ungerade ist.

Mit vielen Routinepfaden füllt der Wicklungsalgorithmus häufig alle eingeschlossenen Bereiche eines Pfads. Der gerade ungerade Algorithmus erzeugt in der Regel interessantere Ergebnisse.

Das klassische Beispiel ist ein fünfzackige Stern, wie auf der Seite mit fünf Spitzenstern gezeigt. Die Datei FivePointedStarPage.xaml instanziiert zwei Picker Ansichten, um den Pfadfülltyp auszuwählen und ob der Pfad Striche oder gefüllt oder beides ist und in welcher Reihenfolge:

<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>

Die CodeBehind-Datei verwendet beide Picker Werte, um einen fünfzackigen Stern zu zeichnen:

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

Normalerweise sollte sich der Pfadfülltyp nur auf Füllungen und nicht auf Striche auswirken, aber die beiden Inverse Modi wirken sich sowohl auf Füllungen als auch auf Striche aus. Bei Füllungen füllen die beiden Inverse Typen Bereiche gegenüber, sodass der Bereich außerhalb des Sterns gefüllt ist. Bei Strichen färben die beiden Inverse Typen alles mit Ausnahme des Strichs. Die Verwendung dieser umgekehrten Fülltypen kann einige ungerade Effekte erzeugen, wie der iOS-Screenshot veranschaulicht:

Dreifacher Screenshot der Seite

Der Android-Screenshot zeigt die typischen Gleichmäßigkeits- und Wicklungseffekte, aber die Reihenfolge des Strichs und der Füllung wirkt sich auch auf die Ergebnisse aus.

Der Wicklungsalgorithmus hängt von der Richtung ab, in der Linien gezeichnet werden. Normalerweise können Sie beim Erstellen eines Pfads diese Richtung steuern, während Sie angeben, dass Linien von einem Punkt zu einem anderen gezeichnet werden. SKPath Die Klasse definiert jedoch auch Methoden wie AddRect und AddCircle zeichnet ganze Konturen. Um zu steuern, wie diese Objekte gezeichnet werden, enthalten die Methoden einen Parameter vom Typ SKPathDirection, der über zwei Member verfügt:

  • Clockwise
  • CounterClockwise

Die Methoden, die SKPath einen SKPathDirection Parameter enthalten, geben ihm einen Standardwert von Clockwise.

Die Seite "Überlappende Kreise " erstellt einen Pfad mit vier überlappenden Kreisen mit einem geraden Pfadfülltyp:

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

Es ist ein interessantes Bild, das mit mindestens einem Code erstellt wurde:

Dreifacher Screenshot der Seite