Поделиться через


Линии и концы штрихов

Узнайте, как использовать SkiaSharp для рисования линий с различными крышками штрихов

В SkiaSharp отрисовка одной строки отличается от от отрисовки ряда подключенных прямых линий. Даже при рисовании отдельных линий, однако, часто необходимо дать линии определенной ширины штриха. По мере того как эти линии становятся более широкими, внешний вид концы линий также становится важным. Внешний вид конца линии называется крышкой штриха:

Три варианта штрихов

Для рисования отдельных строк определяет простой DrawLine метод, SKCanvas аргументы которого указывают начальные и конечные координаты строки с SKPaint объектом:

canvas.DrawLine (x0, y0, x1, y1, paint);

По умолчанию StrokeWidth свойство нового экземпляра SKPaint объекта равно 0, которое имеет тот же эффект, что и значение 1 при отрисовке строки одного пикселя в толщине. Это выглядит очень тонко на устройствах с высоким разрешением, таких как телефоны, поэтому вы, вероятно, хотите задать StrokeWidth большее значение. Но после начала рисования линий изменяемой толщины, которая вызывает другую проблему: как должны быть отрисованы начальные и концы этих толстых линий?

Внешний вид стартов и концы линий называется крышкой линии или, в Skia, штриховой крышкой. Слово "cap" в этом контексте относится к типу шляпы - то, что сидит в конце строки. Для свойства SKPaint объекта задано StrokeCap одно из следующих элементов SKStrokeCap перечисления:

  • Butt (значение по умолчанию)
  • Square
  • Round

Это лучше всего иллюстрируется примером программы. Раздел SkiaSharp Lines и Paths примера программы начинается со страницы с заголовком caps на основе StrokeCapsPage класса. На этой странице определяется PaintSurface обработчик событий, который циклит по трем элементам SKStrokeCap перечисления, отображая имя элемента перечисления и рисуя строку с помощью этой крышки штриха:

void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
    SKImageInfo info = args.Info;
    SKSurface surface = args.Surface;
    SKCanvas canvas = surface.Canvas;

    canvas.Clear();

    SKPaint textPaint = new SKPaint
    {
        Color = SKColors.Black,
        TextSize = 75,
        TextAlign = SKTextAlign.Center
    };

    SKPaint thickLinePaint = new SKPaint
    {
        Style = SKPaintStyle.Stroke,
        Color = SKColors.Orange,
        StrokeWidth = 50
    };

    SKPaint thinLinePaint = new SKPaint
    {
        Style = SKPaintStyle.Stroke,
        Color = SKColors.Black,
        StrokeWidth = 2
    };

    float xText = info.Width / 2;
    float xLine1 = 100;
    float xLine2 = info.Width - xLine1;
    float y = textPaint.FontSpacing;

    foreach (SKStrokeCap strokeCap in Enum.GetValues(typeof(SKStrokeCap)))
    {
        // Display text
        canvas.DrawText(strokeCap.ToString(), xText, y, textPaint);
        y += textPaint.FontSpacing;

        // Display thick line
        thickLinePaint.StrokeCap = strokeCap;
        canvas.DrawLine(xLine1, y, xLine2, y, thickLinePaint);

        // Display thin line
        canvas.DrawLine(xLine1, y, xLine2, y, thinLinePaint);
        y += 2 * textPaint.FontSpacing;
    }
}

Для каждого элемента SKStrokeCap перечисления обработчик рисует две линии, один с толщиной штриха 50 пикселей, а другая линия, расположенная поверх толщины штриха двух пикселей. Эта вторая строка предназначена для иллюстрации геометрического начала и конца линии независимо от толщины линии и штриховой крышки:

Тройной снимок экрана: страница

Как видно, Square Round и крышки штрихов эффективно расширяют длину линии на половину ширины штриха в начале линии и снова в конце. Это расширение становится важным, когда необходимо определить размеры отрисованного графического объекта.

Класс SKCanvas также включает другой метод для рисования нескольких строк, которые несколько своеобразны:

DrawPoints (SKPointMode mode, points, paint)

Параметр points представляет собой массив значений SKPoint и mode является членом SKPointMode перечисления, который содержит три элемента:

  • Points отображение отдельных точек
  • Lines подключение каждой пары точек
  • Polygon подключение всех последовательных точек

На странице "Несколько строк" показан этот метод. Файл MultipleLinesPage.xaml создает экземпляры двух Picker представлений, которые позволяют выбрать элемент SKPointMode перечисления и элемент SKStrokeCap перечисления:

<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.MultipleLinesPage"
             Title="Multiple Lines">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <Picker x:Name="pointModePicker"
                Title="Point Mode"
                Grid.Row="0"
                Grid.Column="0"
                SelectedIndexChanged="OnPickerSelectedIndexChanged">
            <Picker.ItemsSource>
                <x:Array Type="{x:Type skia:SKPointMode}">
                    <x:Static Member="skia:SKPointMode.Points" />
                    <x:Static Member="skia:SKPointMode.Lines" />
                    <x:Static Member="skia:SKPointMode.Polygon" />
                </x:Array>
            </Picker.ItemsSource>
            <Picker.SelectedIndex>
                0
            </Picker.SelectedIndex>
        </Picker>

        <Picker x:Name="strokeCapPicker"
                Title="Stroke Cap"
                Grid.Row="0"
                Grid.Column="1"
                SelectedIndexChanged="OnPickerSelectedIndexChanged">
            <Picker.ItemsSource>
                <x:Array Type="{x:Type skia:SKStrokeCap}">
                    <x:Static Member="skia:SKStrokeCap.Butt" />
                    <x:Static Member="skia:SKStrokeCap.Round" />
                    <x:Static Member="skia:SKStrokeCap.Square" />
                </x:Array>
            </Picker.ItemsSource>
            <Picker.SelectedIndex>
                0
            </Picker.SelectedIndex>
        </Picker>

        <skiaforms:SKCanvasView x:Name="canvasView"
                                PaintSurface="OnCanvasViewPaintSurface"
                                Grid.Row="1"
                                Grid.Column="0"
                                Grid.ColumnSpan="2" />
    </Grid>
</ContentPage>

Обратите внимание, что объявления пространства имен SkiaSharp немного отличаются, так как SkiaSharp пространство имен необходимо для ссылки на элементы SKPointMode и SKStrokeCap перечисления. Обработчик SelectedIndexChanged обоих Picker представлений просто делает объект недействительным SKCanvasView :

void OnPickerSelectedIndexChanged(object sender, EventArgs args)
{
    if (canvasView != null)
    {
        canvasView.InvalidateSurface();
    }
}

Этот обработчик должен проверка для существования SKCanvasView объекта, так как обработчик событий сначала вызывается, когда SelectedIndex свойство Picker объекта имеет значение 0 в XAML-файле и возникает до SKCanvasView создания экземпляра.

Обработчик PaintSurface получает два значения перечисления из представлений Picker :

void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
    SKImageInfo info = args.Info;
    SKSurface surface = args.Surface;
    SKCanvas canvas = surface.Canvas;

    canvas.Clear();

    // Create an array of points scattered through the page
    SKPoint[] points = new SKPoint[10];

    for (int i = 0; i < 2; i++)
    {
        float x = (0.1f + 0.8f * i) * info.Width;

        for (int j = 0; j < 5; j++)
        {
            float y = (0.1f + 0.2f * j) * info.Height;
            points[2 * j + i] = new SKPoint(x, y);
        }
    }

    SKPaint paint = new SKPaint
    {
        Style = SKPaintStyle.Stroke,
        Color = SKColors.DarkOrchid,
        StrokeWidth = 50,
        StrokeCap = (SKStrokeCap)strokeCapPicker.SelectedItem
    };

    // Render the points by calling DrawPoints
    SKPointMode pointMode = (SKPointMode)pointModePicker.SelectedItem;
    canvas.DrawPoints(pointMode, points, paint);
}

На снимках экрана показаны различные Picker варианты выбора:

Тройной снимок экрана: страница

I Телефон слева показывает, как SKPointMode.Points член перечисления вызывает DrawPoints отрисовку каждой точки в SKPoint массиве в виде квадрата, если крышка Butt строки илиSquare. Круги отрисовываются, если ограничение строки равно Round.

Снимок экрана с Android показывает результат SKPointMode.Lines. Метод DrawPoints рисует линию между каждой парой значений SKPoint , используя указанную крышку строки в данном случае Round.

Вместо этого SKPointMode.Polygonлиния рисуется между последовательными точками в массиве, но при внимательном просмотре вы увидите, что эти линии не подключены. Каждая из этих отдельных строк начинается и заканчивается указанным ограничением строки. Если выбрать Round крышки, линии могут быть подключены, но они действительно не подключены.

Независимо от того, подключены ли линии, являются важным аспектом работы с графическими путями.