Типы заполнения пути
Обнаружение различных эффектов с помощью типов заливки пути SkiaSharp
Два контура в пути могут перекрываться, а линии, составляющие один контур, могут перекрываться. Любая заключенная область может быть заполнена, но может не потребоваться заполнить все заключенные области. Приведем пример:
У вас есть немного контроля над этим. Алгоритм заполнения управляется свойством SKFillType
SKPath
, заданным для элемента 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:
На снимках экрана 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);
}
Это интересный образ, созданный с минимальным кодом: