Tipi di riempimento dei tracciati

Download Sample Scaricare l'esempio

Scopri i diversi effetti possibili con i tipi di riempimento del percorso SkiaSharp

Due contorni in un percorso possono sovrapporsi e le linee che costituiscono un singolo contorno possono sovrapporsi. Qualsiasi area racchiusa può essere potenzialmente riempita, ma potresti non voler riempire tutte le aree racchiuse. Ecco un esempio:

Five-pointed star partially filles

Hai un po' di controllo su questo. L'algoritmo di riempimento è regolato dalla SKFillType proprietà di SKPath, impostata su un membro dell'enumerazione SKPathFillType :

  • Winding, impostazione predefinita
  • EvenOdd
  • InverseWinding
  • InverseEvenOdd

Sia gli algoritmi di avvolgimento che dispari determinano se un'area racchiusa viene riempita o non riempita in base a una linea ipotetica disegnata da quell'area all'infinito. Tale linea attraversa una o più linee limite che costituiscono il percorso. Con la modalità di avvolgimento, se il numero di linee limite disegnate in una direzione bilancia il numero di linee disegnate nell'altra direzione, l'area non viene riempita. In caso contrario, l'area viene riempita. L'algoritmo dispari pari riempie un'area se il numero di linee limite è dispari.

Con molti percorsi di routine, l'algoritmo di avvolgimento spesso riempie tutte le aree racchiuse di un percorso. L'algoritmo dispari in genere produce risultati più interessanti.

L'esempio classico è una stella a cinque punte, come illustrato nella pagina A stella a cinque punte. Il file FivePointedStarPage.xaml crea un'istanza di due Picker visualizzazioni per selezionare il tipo di riempimento del percorso e se il percorso viene tracciato o riempito o entrambi e in quale ordine:

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

Il file code-behind usa entrambi Picker i valori per disegnare una stella a cinque punte:

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

In genere, il tipo di riempimento del percorso dovrebbe influire solo sui riempimenti e non sui tratti, ma le due Inverse modalità influiscono sia sui riempimenti che sui tratti. Per i riempimenti, i due Inverse tipi riempiono le aree in modo opposto in modo che l'area esterna alla stella sia riempita. Per i tratti, i due Inverse tipi colorano tutto tranne il tratto. L'uso di questi tipi di riempimento inverso può produrre alcuni effetti dispari, come illustrato nello screenshot di iOS:

Triple screenshot of the Five-Pointed Star page

Lo screenshot di Android mostra i tipici effetti dispari e avvolgimenti, ma l'ordine del tratto e del riempimento influisce anche sui risultati.

L'algoritmo di avvolgimento dipende dalla direzione di disegno delle linee. In genere, quando si crea un percorso, è possibile controllare tale direzione quando si specifica che le linee vengono disegnate da un punto all'altro. Tuttavia, la SKPath classe definisce anche metodi come AddRect e AddCircle che disegnano interi contorni. Per controllare la modalità di disegno di questi oggetti, i metodi includono un parametro di tipo SKPathDirection, che ha due membri:

  • Clockwise
  • CounterClockwise

I metodi in SKPath che includono un SKPathDirection parametro assegnano un valore predefinito di Clockwise.

La pagina Cerchi sovrapposti crea un percorso con quattro cerchi sovrapposti con un tipo di riempimento di percorso pari-spari:

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

Si tratta di un'immagine interessante creata con un minimo di codice:

Triple screenshot of the Overlapping Circles page