Linien und Strichenden

Beispiel herunterladen Das Beispiel herunterladen

Erfahren Sie, wie Sie SkiaSharp verwenden, um Linien mit verschiedenen Strichkappen zu zeichnen.

In SkiaSharp unterscheidet sich das Rendern einer einzelnen Linie stark vom Rendern einer Reihe verbundener gerader Linien. Auch beim Zeichnen einzelner Linien ist es jedoch häufig notwendig, den Linien eine bestimmte Strichbreite zu verleihen. Wenn diese Linien breiter werden, wird auch das Aussehen der Enden der Linien wichtig. Das Aussehen des Zeilenendes wird als Strichkappe bezeichnet:

Die Drei Strichkappenoptionen

Definiert zum Zeichnen einzelner Linien eine einfache DrawLine Methode, SKCanvas deren Argumente die Anfangs- und Endkoordinaten der Linie mit einem SKPaint -Objekt angeben:

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

Standardmäßig ist die StrokeWidth Eigenschaft eines neu instanziierten SKPaint Objekts 0. Dies hat die gleiche Auswirkung wie ein Wert von 1 beim Rendern einer Linie mit einer Stärke von einem Pixel. Dies erscheint auf hochauflösenden Geräten wie Smartphones sehr dünn, sodass Sie wahrscheinlich einen StrokeWidth größeren Wert festlegen möchten. Aber sobald Sie mit dem Zeichnen von Linien mit einer beträchtlichen Stärke beginnen, wirft das eine andere Frage auf: Wie sollten die Anfangs- und Enden dieser dicken Linien gerendert werden?

Die Darstellung der Anfangs- und Enden von Linien wird als Linienkappe oder in Skia als Strichkappe bezeichnet. Das Wort "Cap" bezieht sich in diesem Kontext auf eine Art Hut – etwas, das am Ende der Zeile sitzt. Sie legen die StrokeCap -Eigenschaft des SKPaint -Objekts auf eines der folgenden Member der SKStrokeCap Enumeration fest:

  • Butt (Standardeinstellung)
  • Square
  • Round

Diese lassen sich am besten mit einem Beispielprogramm veranschaulichen. Der Abschnitt SkiaSharp Lines and Paths des SkiaSharpFormsDemos-Programms beginnt mit einer Seite mit dem Titel Strichkappen basierend auf der StrokeCapsPage Klasse. Diese Seite definiert einen PaintSurface Ereignishandler, der die drei Member der SKStrokeCap Enumeration durchläuft und sowohl den Namen des Enumerationsmembers anzeigt als auch eine Linie mit dieser Strichkappe zeichnet:

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

Für jedes Element der SKStrokeCap Enumeration zeichnet der Handler zwei Linien, eine mit einer Strichstärke von 50 Pixeln und eine weitere Linie, die oben mit einer Strichstärke von zwei Pixeln positioniert ist. Diese zweite Zeile soll den geometrischen Anfang und das Ende der Linie unabhängig von der Linienstärke und einer Strichkappe veranschaulichen:

Wie Sie sehen können, verlängern die Square Strichkappen und Round die Länge der Linie effektiv um die Hälfte der Strichbreite am Anfang der Linie und wieder am Ende. Diese Erweiterung wird wichtig, wenn die Dimensionen eines gerenderten Grafikobjekts bestimmt werden müssen.

Die SKCanvas -Klasse enthält auch eine andere Methode zum Zeichnen mehrerer Linien, die etwas eigenartig ist:

DrawPoints (SKPointMode mode, points, paint)

Der points Parameter ist ein Array von SKPoint Werten und mode ist ein Member der SKPointMode Enumeration, die drei Member enthält:

  • Points , um die einzelnen Punkte zu rendern
  • Lines , um jedes Punktpaar zu verbinden
  • Polygon , um alle aufeinanderfolgenden Punkte zu verbinden

Auf der Seite Mehrere Zeilen wird diese Methode veranschaulicht. Die Datei MultipleLinesPage.xaml instanziiert zwei Picker Ansichten, mit denen Sie ein Element der SKPointMode Enumeration und ein Element der SKStrokeCap Enumeration auswählen können:

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

Beachten Sie, dass sich die SkiaSharp-Namespacedeklarationen etwas unterscheiden, da der SkiaSharp Namespace benötigt wird, um auf die Member der SKPointMode Enumerationen und SKStrokeCap zu verweisen. Der SelectedIndexChanged Handler für beide Picker Ansichten ungültig das SKCanvasView Objekt einfach:

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

Dieser Handler muss überprüfen, ob das SKCanvasView -Objekt vorhanden ist, da der Ereignishandler zuerst aufgerufen wird, wenn die SelectedIndex Eigenschaft von Picker in der XAML-Datei auf 0 festgelegt ist, und dies geschieht, bevor das SKCanvasView instanziiert wurde.

Der PaintSurface Handler ruft die beiden Enumerationswerte aus den Picker Ansichten ab:

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

Die Screenshots zeigen eine Vielzahl von Picker Auswahlmöglichkeiten:

Das iPhone auf der linken Seite zeigt, wie der SKPointMode.Points Enumerationsmember bewirktDrawPoints, dass jeder der Punkte im SKPoint Array als Quadrat gerendert wird, wenn die Linienkappe oder SquareistButt. Kreise werden gerendert, wenn die Linienkappe ist Round.

Der Android-Screenshot zeigt das Ergebnis von SKPointMode.Lines. Die DrawPoints -Methode zeichnet eine Linie zwischen jedem Wertepaar SKPoint , wobei die angegebene Zeilenobergrenze verwendet wird, in diesem Fall Round.

Wenn Sie stattdessen verwenden SKPointMode.Polygon, wird eine Linie zwischen den aufeinanderfolgenden Punkten im Array gezeichnet, aber wenn Sie genau hinsehen, werden Sie sehen, dass diese Linien nicht verbunden sind. Jede dieser separaten Zeilen beginnt und endet mit der angegebenen Zeilenobergrenze. Wenn Sie die Round Obergrenzen auswählen, scheinen die Linien möglicherweise verbunden zu sein, aber sie sind wirklich nicht verbunden.

Ob Linien verbunden sind oder nicht, ist ein entscheidender Aspekt beim Arbeiten mit Grafikpfaden.