Types de remplissage des tracés

Télécharger l’exemple Télécharger l’exemple

Découvrir les différents effets possibles avec les types de remplissage de chemin d’accès SkiaSharp

Deux contours d’un chemin peuvent se chevaucher, et les lignes qui composent un seul contour peuvent se chevaucher. Toute zone fermée peut potentiellement être remplie, mais vous ne souhaiterez peut-être pas remplir toutes les zones fermées. Voici un exemple :

Star à cinq branches partiellement filles

Vous avez un peu de contrôle sur ça. L’algorithme de remplissage est régi par la SKFillType propriété de SKPath, que vous définissez sur un membre de l’énumération SKPathFillType :

  • Winding, valeur par défaut
  • EvenOdd
  • InverseWinding
  • InverseEvenOdd

Les algorithmes d’enroulement et d’impairs pairs déterminent si une zone fermée est remplie ou non remplie en fonction d’une ligne hypothétique tirée de cette zone vers l’infini. Cette ligne traverse une ou plusieurs lignes limites qui composent le chemin d’accès. Avec le mode enroulement, si le nombre de lignes limites dessinées dans une direction équilibre le nombre de lignes dessinées dans l’autre sens, la zone n’est pas remplie. Sinon, la zone est remplie. L’algorithme pair-impair remplit une zone si le nombre de lignes limites est impair.

Avec de nombreux chemins de routine, l’algorithme de bobage remplit souvent toutes les zones fermées d’un chemin. L’algorithme pair-impair produit généralement des résultats plus intéressants.

L’exemple classique est une star à cinq branches, comme illustré dans la page Étoile à cinq branches. Le fichier FivePointedStarPage.xaml instancie deux Picker affichages pour sélectionner le type de remplissage du chemin d’accès et déterminer si le chemin est tracé ou rempli ou les deux, et dans quel ordre :

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

Le fichier code-behind utilise les deux Picker valeurs pour dessiner un star à cinq points :

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

Normalement, le type de remplissage de chemin doit affecter uniquement les remplissages et non les traits, mais les deux Inverse modes affectent à la fois les remplissages et les traits. Pour les remplissages, les deux Inverse types remplissent des zones opposées afin que la zone située à l’extérieur du star soit remplie. Pour les traits, les deux Inverse types colorent tout sauf le trait. L’utilisation de ces types de remplissage inverse peut produire des effets étranges, comme le montre la capture d’écran iOS :

Capture d’écran Triple de la page Five-Pointed Star

La capture d’écran Android montre les effets pairs et sinistrés typiques, mais l’ordre du trait et du remplissage affecte également les résultats.

L’algorithme d’enroulement dépend de la direction dans laquelle les lignes sont dessinées. En règle générale, lorsque vous créez un chemin d’accès, vous pouvez contrôler cette direction en spécifiant que les lignes sont dessinées d’un point à un autre. Toutefois, la SKPath classe définit également des méthodes telles AddRect que et AddCircle qui dessinent des contours entiers. Pour contrôler la façon dont ces objets sont dessinés, les méthodes incluent un paramètre de type SKPathDirection, qui a deux membres :

  • Clockwise
  • CounterClockwise

Les méthodes dans SKPath qui incluent un SKPathDirection paramètre lui donnent la valeur par défaut .Clockwise

La page Cercles de chevauchement crée un chemin avec quatre cercles qui se chevauchent avec un type de remplissage de chemin impair pair :

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

Il s’agit d’une image intéressante créée avec un minimum de code :

Triple capture d’écran de la page Cercles qui se chevauchent