Partilhar via


Limites de linha e de traço

Saiba como usar o SkiaSharp para desenhar linhas com diferentes limites de traçado

No SkiaSharp, renderizar uma única linha é muito diferente de renderizar uma série de linhas retas conectadas. Mesmo ao desenhar linhas únicas, no entanto, muitas vezes é necessário dar às linhas uma largura de traçado específica. À medida que essas linhas se tornam mais largas, a aparência das extremidades das linhas também se torna importante. A aparência do final da linha é chamada de tampa de curso:

As opções de tampas de três tempos

Para desenhar linhas únicas, SKCanvas define um método simples DrawLine cujos argumentos indicam as coordenadas inicial e final da linha com um SKPaint objeto:

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

Por padrão, a StrokeWidth propriedade de um objeto recém-instanciado SKPaint é 0, que tem o mesmo efeito que um valor de 1 na renderização de uma linha de um pixel de espessura. Isso parece muito fino em dispositivos de alta resolução, como telefones, então você provavelmente vai querer definir o StrokeWidth para um valor maior. Mas uma vez que você começa a desenhar linhas de uma espessura considerável, isso levanta outra questão: como os começos e os fins dessas linhas grossas devem ser renderizados?

A aparência dos inícios e fins das linhas é chamada de tampa de linha ou, em Skia, de tampa de curso. A palavra "boné", nesse contexto, refere-se a uma espécie de chapéu – algo que fica no fim da linha. Você define a StrokeCap SKPaint propriedade do objeto para um dos seguintes membros da SKStrokeCap enumeração:

  • Butt (o padrão)
  • Square
  • Round

Estes são melhor ilustrados com um programa de exemplo. A seção SkiaSharp Lines and Paths do programa de exemplo começa com uma página intitulada Stroke Caps com base na StrokeCapsPage classe. Esta página define um PaintSurface manipulador de eventos que percorre os três membros da enumeração, exibindo o nome do membro da enumeração e desenhando uma linha usando essa tampa de SKStrokeCap traçado:

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

Para cada membro da SKStrokeCap enumeração, o manipulador desenha duas linhas, uma com uma espessura de traçado de 50 pixels e outra linha posicionada na parte superior com uma espessura de traçado de dois pixels. Esta segunda linha destina-se a ilustrar o início e o fim geométricos da linha independentemente da espessura da linha e de uma tampa de curso:

Captura de tela tripla da página Stroke Caps

Como você pode ver, as Square Round tampas e traçado efetivamente estendem o comprimento da linha pela metade da largura do curso no início da linha e novamente no final. Essa extensão se torna importante quando é necessário determinar as dimensões de um objeto gráfico renderizado.

A SKCanvas classe também inclui outro método para desenhar várias linhas que é um pouco peculiar:

DrawPoints (SKPointMode mode, points, paint)

O points parâmetro é uma matriz de SKPoint valores e mode é um membro da SKPointMode enumeração, que tem três membros:

  • Points para renderizar os pontos individuais
  • Lines para conectar cada par de pontos
  • Polygon para conectar todos os pontos consecutivos

A página Várias linhas demonstra esse método. O arquivo MultipleLinesPage.xaml instancia dois Picker modos de exibição que permitem selecionar um membro da SKPointMode enumeração e um membro da SKStrokeCap enumeração:

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

Observe que as declarações de namespace SkiaSharp são um pouco diferentes porque o SkiaSharp namespace é necessário para fazer referência aos membros das SKPointMode enumerações e SKStrokeCap . O SelectedIndexChanged manipulador para ambos os Picker modos de exibição simplesmente invalida o SKCanvasView objeto:

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

Esse manipulador precisa verificar a existência do SKCanvasView objeto porque o manipulador de eventos é chamado pela primeira vez quando a SelectedIndex propriedade do é definida como 0 no arquivo XAML e isso ocorre antes de Picker o SKCanvasView ter sido instanciado.

O PaintSurface manipulador obtém os dois valores de enumeração dos Picker modos de exibição:

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

As capturas de tela mostram uma variedade de Picker seleções:

Captura de tela tripla da página Várias Linhas

O iPhone à esquerda mostra como o membro de SKPointMode.Points enumeração faz com que DrawPoints cada um dos pontos na SKPoint matriz seja um quadrado se a tampa de linha for Butt ou Square. Os círculos serão renderizados se a tampa da linha for Round.

A captura de tela do Android mostra o resultado do SKPointMode.Lines. O DrawPoints método desenha uma linha entre cada par de valores, usando a tampa de SKPoint linha especificada, neste caso Round.

Quando você usa SKPointMode.Polygono , uma linha é desenhada entre os pontos sucessivos na matriz, mas se você olhar muito de perto, verá que essas linhas não estão conectadas. Cada uma dessas linhas separadas começa e termina com a tampa de linha especificada. Se você selecionar as Round tampas, as linhas podem parecer conectadas, mas elas realmente não estão conectadas.

Se as linhas estão conectadas ou não conectadas é um aspecto crucial do trabalho com caminhos gráficos.