Compartilhar via


Os tipos de preenchimento do caminho

Descubra os diferentes efeitos possíveis com os tipos de preenchimento de caminho SkiaSharp

Dois contornos em um caminho podem se sobrepor, e as linhas que compõem um único contorno podem se sobrepor. Qualquer área fechada pode ser potencialmente preenchida, mas talvez você não queira preencher todas as áreas fechadas. Veja um exemplo:

Estrela de cinco pontas preenche parcialmente

Você tem um pouco de controle sobre isso. O algoritmo de preenchimento é governado SKFillType pela propriedade de SKPath, que você define como um membro da SKPathFillType enumeração:

  • Winding, o padrão
  • EvenOdd
  • InverseWinding
  • InverseEvenOdd

Tanto o enrolamento quanto os algoritmos pares determinam se alguma área fechada é preenchida ou não com base em uma linha hipotética traçada dessa área até o infinito. Essa linha cruza uma ou mais linhas de fronteira que compõem o caminho. Com o modo de enrolamento, se o número de linhas de contorno desenhadas em uma direção equilibrar o número de linhas desenhadas na outra direção, então a área não é preenchida. Caso contrário, a área é preenchida. O algoritmo par-ímpar preenche uma área se o número de linhas de contorno for ímpar.

Com muitos caminhos de rotina, o algoritmo de enrolamento geralmente preenche todas as áreas fechadas de um caminho. O algoritmo par-ímpar geralmente produz resultados mais interessantes.

O exemplo clássico é uma estrela de cinco pontas, como demonstrado na página Estrela de cinco pontas. O arquivo FivePointedStarPage.xaml instancia dois Picker modos de exibição para selecionar o tipo de preenchimento de caminho e se o caminho é traçado ou preenchido ou ambos, e em que ordem:

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

O arquivo code-behind usa ambos os Picker valores para desenhar uma estrela de cinco pontas:

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

Normalmente, o tipo de preenchimento de caminho deve afetar apenas preenchimentos e não traçados, mas os dois Inverse modos afetam preenchimentos e traçados. Para preenchimentos, os dois Inverse tipos preenchem áreas opostas para que a área fora da estrela seja preenchida. Para traçados, os dois Inverse tipos colorem tudo, exceto o traçado. Usar esses tipos de preenchimento inverso pode produzir alguns efeitos estranhos, como demonstra a captura de tela do iOS:

Captura de tela tripla da página Estrela de Cinco Pontas

A captura de tela do Android mostra os efeitos típicos pares e sinuosos, mas a ordem do traçado e do preenchimento também afeta os resultados.

O algoritmo de enrolamento depende da direção em que as linhas são desenhadas. Normalmente, ao criar um caminho, você pode controlar essa direção ao especificar que as linhas são desenhadas de um ponto a outro. No entanto, a SKPath classe também define métodos como AddRect e AddCircle que desenham contornos inteiros. Para controlar como esses objetos são desenhados, os métodos incluem um parâmetro do tipo SKPathDirection, que tem dois membros:

  • Clockwise
  • CounterClockwise

Os métodos que SKPath incluem um SKPathDirection parâmetro fornecem um valor padrão de Clockwise.

A página Círculos sobrepostos cria um caminho com quatro círculos sobrepostos com um tipo de preenchimento de caminho par ímpar:

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

É uma imagem interessante criada com um mínimo de código:

Captura de tela tripla da página Círculos sobrepostos