Типы заполнения пути

Download Sample Скачайте пример

Обнаружение различных эффектов с помощью типов заливки пути SkiaSharp

Два контура в пути могут перекрываться, а линии, составляющие один контур, могут перекрываться. Любая заключенная область может быть заполнена, но может не потребоваться заполнить все заключенные области. Приведем пример:

Five-pointed star partially filles

У вас есть немного контроля над этим. Алгоритм заполнения управляется свойством SKFillTypeSKPath, заданным для элемента SKPathFillType перечисления:

  • Winding (стандартный вариант);
  • EvenOdd
  • InverseWinding
  • InverseEvenOdd

Оба алгоритма ветвления и даже нечетные алгоритмы определяют, заполняется ли любая заключенная область или не заполнена на основе гипотетической линии, нарисованной из этой области до бесконечности. Эта линия пересекает одну или несколько линий границ, составляющих путь. Если число границ, нарисованных в одном направлении, сбалансируйте количество линий, нарисованных в другом направлении, то область не заполняется. В противном случае область заполняется. Нечетный алгоритм заполняет область, если число линий границ нечетно.

С множеством стандартных путей алгоритм обмотки часто заполняет все заключенные области пути. Даже нечетный алгоритм обычно выдает более интересные результаты.

Классический пример представляет собой пятиконечную звезду, как показано на странице "Пятиконечная звезда ". Файл FivePointedStarPage.xaml создает экземпляры двух 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;
    }
}

Как правило, тип заливки пути должен влиять только на заливки и не штрихи, но два Inverse режима влияют как на заливки, так и на штрихи. Для заливок два Inverse типа заполняют области напротив, чтобы область за пределами звезды заполнена. Для штрихов два Inverse типа цвета все, кроме штриха. Использование этих обратных типов заливок может создавать некоторые странные эффекты, как показано на снимке экрана iOS:

Triple screenshot of the Five-Pointed Star page

На снимках экрана Android показаны типичные эффекты равномерного и ветвления, но порядок штриха и заливки также влияет на результаты.

Алгоритм обмотки зависит от направления, в который рисуются линии. Обычно при создании пути можно контролировать это направление, указывая, что линии рисуются из одной точки в другую. Однако класс также определяет методы, как AddRect и AddCircle то, SKPath что рисует целые контуры. Чтобы управлять нарисовкой этих объектов, методы включают параметр типа SKPathDirection, который имеет два члена:

  • Clockwise
  • CounterClockwise

Методы, содержащие параметр SKPath , SKPathDirection дают ему значение Clockwiseпо умолчанию.

Страница "Перекрывающиеся круги" создает путь с четырьмя перекрывающимися кругами с типом заливки четного пути:

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

Это интересный образ, созданный с минимальным кодом:

Triple screenshot of the Overlapping Circles page