Teilen über


Linien und Strichenden

Erfahren Sie, wie Sie Mit SkiaSharp Linien mit unterschiedlichen Strichkappen zeichnen.

In SkiaSharp unterscheidet sich das Rendern einer einzelnen Zeile von der Darstellung einer Reihe verbundener gerader Linien. Auch beim Zeichnen einzelner Linien ist es häufig erforderlich, den Linien eine bestimmte Strichbreite zu verleihen. Da diese Linien breiter werden, wird auch das Aussehen der Enden der Linien wichtig. Die Darstellung des Endes der Linie wird als Strichkappe bezeichnet:

Die drei Strichkappenoptionen

Zum Zeichnen einzelner Linien definiert 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, das denselben Effekt hat wie ein Wert von 1 beim Rendern einer Zeile mit einer Pixelstärke. Dies erscheint auf geräten mit hoher Auflösung wie Smartphones sehr dünn, sodass Sie wahrscheinlich den StrokeWidth Wert auf einen größeren Wert festlegen möchten. Aber sobald Sie mit dem Zeichnen von Linien einer izierbaren Stärke beginnen, löst dies ein weiteres Problem aus: Wie sollten die Anfangs- und Enden dieser dicken Linien gerendert werden?

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

  • Butt (Standard)
  • Square
  • Round

Diese werden am besten mit einem Beispielprogramm veranschaulicht. Der Abschnitt "SkiaSharp Lines and Paths " des Beispielprogramms beginnt mit einer Seite mit dem Titel "Stroke Caps " basierend auf der StrokeCapsPage Klasse. Diese Seite definiert einen PaintSurface Ereignishandler, der die drei Member der SKStrokeCap Enumeration durchläuft, wobei sowohl der Name des Enumerationsmembers als auch das Zeichnen einer Linie mit dieser Strichkappe angezeigt wird:

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 Aufzählung zeichnet der Handler zwei Linien, eine mit einer Strichstärke von 50 Pixeln und eine andere 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:

Dreifacher Screenshot der Seite

Wie Sie sehen können, erweitern die Groß- und Round Strichkappen die Square Länge der Linie um die Hälfte der Strichbreite am Anfang der Linie und wieder am Ende. Diese Erweiterung wird wichtig, wenn es erforderlich ist, die Dimensionen eines gerenderten Grafikobjekts zu bestimmen.

Die SKCanvas Klasse enthält auch eine weitere 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 ein Element der SKPointMode Enumeration, das drei Elemente enthält:

  • Points so rendern Sie die einzelnen Punkte
  • Lines zum Verbinden der einzelnen Punktepaare
  • Polygon so verbinden Sie alle aufeinander folgenden Punkte

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 die SkiaSharp-Namespacedeklarationen etwas anders sind, da der SkiaSharp Namespace benötigt wird, um auf die Member der SKPointMode Und SKStrokeCap Enumerationen zu verweisen. Der SelectedIndexChanged Handler für beide Picker Ansichten macht das SKCanvasView Objekt einfach ungültig:

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 des Picker Objekts in der XAML-Datei auf 0 festgelegt ist und vor der SKCanvasView Instanziierung auftritt.

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:

Dreifacher Screenshot der Seite

Die i Telefon links zeigt, wie das Enumerationselement bewirktDrawPoints, dass jeder der Punkte im SKPoint Array als Quadrat gerendert wird, wenn die Linienkappe oder -klammer ist Butt Square.SKPointMode.Points Kreise werden gerendert, wenn die Linienlinie groß ist Round.

Der Android-Screenshot zeigt das Ergebnis des SKPointMode.Lines. Die DrawPoints Methode zeichnet in diesem Fall Roundeine Linie zwischen den einzelnen WertenpaarenSKPoint, wobei die angegebene Linienkappe verwendet wird.

Wenn Sie stattdessen verwenden SKPointMode.Polygon, wird eine Linie zwischen den aufeinander folgenden Punkten im Array gezeichnet, aber wenn Sie sehr genau aussehen, sehen Sie, dass diese Linien nicht verbunden sind. Jede dieser separaten Zeilen beginnt und endet mit der angegebenen Linienkappe. Wenn Sie die Round Feststelltaste auswählen, werden die Linien möglicherweise verbunden, aber sie sind wirklich nicht verbunden.

Ob Linien verbunden sind oder nicht verbunden sind, ist ein wichtiger Aspekt der Arbeit mit Grafikpfaden.