Freigeben über


Anzeigen von SkiaSharp-Bitmaps

Das Thema der SkiaSharp-Bitmaps wurde im Artikel Bitmap Basics in SkiaSharp eingeführt. In diesem Artikel wurden drei Möglichkeiten zum Laden von Bitmaps und drei Möglichkeiten zum Anzeigen von Bitmaps gezeigt. In diesem Artikel werden die Techniken zum Laden von Bitmaps überprüft und die Verwendung der Methoden von DrawBitmapSKCanvas.

Beispiel anzeigen

Die DrawBitmapLattice Methoden und DrawBitmapNinePatch Methoden werden im Artikel Segmented display of SkiaSharp bitmaps erläutert.

Beispiele auf dieser Seite stammen aus der Beispielanwendung. Wählen Sie auf der Startseite dieser Anwendung "SkiaSharp Bitmaps" aus, und wechseln Sie dann zum Abschnitt " Bitmaps anzeigen" .

Laden einer Bitmap

Eine von einer SkiaSharp-Anwendung verwendete Bitmap stammt in der Regel aus einer von drei verschiedenen Quellen:

  • Von über das Internet
  • Aus einer in die ausführbare Datei eingebetteten Ressource
  • Aus der Fotobibliothek des Benutzers

Es ist auch möglich, dass eine SkiaSharp-Anwendung eine neue Bitmap erstellen und dann darauf zeichnen oder die Bitmapbits algorithmisch festlegen kann. Diese Techniken werden in den Artikeln Creating and Drawing on SkiaSharp Bitmaps und Accessing SkiaSharp Bitmap Pixels erläutert.

In den folgenden drei Codebeispielen für das Laden einer Bitmap wird davon ausgegangen, dass die Klasse ein Feld vom Typ SKBitmapenthält:

SKBitmap bitmap;

Wie der Artikel Bitmap Basics in SkiaSharp sagte, ist die beste Möglichkeit, eine Bitmap über das Internet zu laden, mit der HttpClient Klasse. Eine einzelne Instanz der Klasse kann als Feld definiert werden:

HttpClient httpClient = new HttpClient();

Bei Verwendung HttpClient mit iOS- und Android-Anwendungen sollten Sie Projekteigenschaften festlegen, wie in den Dokumenten für Transport Layer Security (TLS) 1.2 beschrieben.

Code, der häufig verwendet wird HttpClient , umfasst den await Operator, daher muss er sich in einer async Methode befinden:

try
{
    using (Stream stream = await httpClient.GetStreamAsync("https:// ··· "))
    using (MemoryStream memStream = new MemoryStream())
    {
        await stream.CopyToAsync(memStream);
        memStream.Seek(0, SeekOrigin.Begin);

        bitmap = SKBitmap.Decode(memStream);
        ···
    };
}
catch
{
    ···
}

Beachten Sie, dass das Stream abgerufene GetStreamAsync Objekt in ein MemoryStream. Android lässt nicht zu, dass die Stream Daten HttpClient vom Standard Thread verarbeitet werden, außer in asynchronen Methoden.

Das SKBitmap.Decode hat viel Arbeit: Das Stream an sie übergebene Objekt verweist auf einen Speicherblock, der eine gesamte Bitmap enthält, in einem der gängigen Bitmapdateiformate, im Allgemeinen JPEG, PNG oder GIF. Die Decode Methode muss das Format bestimmen und dann die Bitmapdatei in das eigene interne Bitmapformat von SkiaSharp decodieren.

Nach dem Aufrufen SKBitmap.Decodedes Codes wird dies CanvasView wahrscheinlich ungültig, sodass der PaintSurface Handler die neu geladene Bitmap anzeigen kann.

Die zweite Möglichkeit zum Laden einer Bitmap besteht darin, dass die Bitmap als eingebettete Ressource in die .NET Standard-Bibliothek eingeschlossen wird, auf die von den einzelnen Plattformprojekten verwiesen wird. An die Methode wird eine GetManifestResourceStream Ressourcen-ID übergeben. Diese Ressourcen-ID besteht aus dem Assemblynamen, dem Ordnernamen und dem Dateinamen der Ressource, die durch Punkte getrennt ist:

string resourceID = "assemblyName.folderName.fileName";
Assembly assembly = GetType().GetTypeInfo().Assembly;

using (Stream stream = assembly.GetManifestResourceStream(resourceID))
{
    bitmap = SKBitmap.Decode(stream);
    ···
}

Bitmapdateien können auch als Ressourcen im einzelnen Plattformprojekt für iOS, Android und die Universelle Windows-Plattform (UWP) gespeichert werden. Das Laden dieser Bitmaps erfordert jedoch Code, der sich im Plattformprojekt befindet.

Ein dritter Ansatz zum Abrufen einer Bitmap stammt aus der Bildbibliothek des Benutzers. Der folgende Code verwendet einen Abhängigkeitsdienst, der in der Beispielanwendung enthalten ist. Die SkiaSharpFormsDemo .NET Standard Library enthält die IPhotoLibrary Schnittstelle, während jedes Plattformprojekt eine PhotoLibrary Klasse enthält, die diese Schnittstelle implementiert.

IPhotoicturePicker picturePicker = DependencyService.Get<IPhotoLibrary>();

using (Stream stream = await picturePicker.GetImageStreamAsync())
{
    if (stream != null)
    {
        bitmap = SKBitmap.Decode(stream);
        ···
    }
}

Im Allgemeinen wird durch diesen Code auch die CanvasView Prozedur ungültig, sodass der PaintSurface Handler die neue Bitmap anzeigen kann.

Die SKBitmap Klasse definiert mehrere nützliche Eigenschaften, einschließlich Width und Height, die die Pixelabmessungen der Bitmap sowie viele Methoden anzeigen, einschließlich Methoden zum Erstellen von Bitmaps, zum Kopieren und Verfügbarmachen der Pixelbits.

Anzeigen in Pixelabmessungen

Die SkiaSharp-Klasse Canvas definiert vier DrawBitmap Methoden. Mit diesen Methoden können Bitmaps grundsätzlich auf unterschiedliche Weise angezeigt werden:

  • Wenn Sie einen SKPoint Wert (oder separate x Werte y ) angeben, wird die Bitmap in ihren Pixelabmessungen angezeigt. Die Pixel der Bitmap werden direkt den Pixeln der Videoanzeige zugeordnet.
  • Wenn Sie ein Rechteck angeben, wird die Bitmap auf die Größe und Form des Rechtecks gestreckt.

Sie zeigen eine Bitmap in ihren Pixelabmessungen mit DrawBitmap einem SKPoint Parameter oder DrawBitmap mit separaten x Parametern an y :

DrawBitmap(SKBitmap bitmap, SKPoint pt, SKPaint paint = null)

DrawBitmap(SKBitmap bitmap, float x, float y, SKPaint paint = null)

Diese beiden Methoden sind funktional identisch. Der angegebene Punkt gibt die Position der oberen linken Ecke der Bitmap relativ zum Zeichenbereich an. Da die Pixelauflösung mobiler Geräte so hoch ist, erscheinen kleinere Bitmaps in der Regel ziemlich klein auf diesen Geräten.

Mit dem optionalen SKPaint Parameter können Sie die Bitmap mithilfe von Transparenz anzeigen. Erstellen Sie dazu ein SKPaint Objekt, und legen Sie die Color Eigenschaft auf einen beliebigen SKColor Wert mit einem Alphakanal unter 1 fest. Zum Beispiel:

paint.Color = new SKColor(0, 0, 0, 0x80);

Die als letztes Argument übergebene 0x80 gibt 50 % Transparenz an. Sie können auch einen Alphakanal für eine der vordefinierten Farben festlegen:

paint.Color = SKColors.Red.WithAlpha(0x80);

Die Farbe selbst ist jedoch irrelevant. Nur der Alphakanal wird untersucht, wenn Sie das SKPaint Objekt in einem DrawBitmap Aufruf verwenden.

Das SKPaint Objekt spielt auch eine Rolle beim Anzeigen von Bitmaps mithilfe von Blendmodi oder Filtereffekten. Diese werden in den Artikeln SkiaSharp Compositing und Blendmodi und SkiaSharp Bildfilter gezeigt.

Die Seite "Pixelabmessungen " im Beispielprogramm zeigt eine Bitmapressource mit einer Breite von 320 Pixeln um 240 Pixel hoch an:

public class PixelDimensionsPage : ContentPage
{
    SKBitmap bitmap;

    public PixelDimensionsPage()
    {
        Title = "Pixel Dimensions";

        // Load the bitmap from a resource
        string resourceID = "SkiaSharpFormsDemos.Media.Banana.jpg";
        Assembly assembly = GetType().GetTypeInfo().Assembly;

        using (Stream stream = assembly.GetManifestResourceStream(resourceID))
        {
            bitmap = SKBitmap.Decode(stream);
        }

        // Create the SKCanvasView and set the PaintSurface handler
        SKCanvasView canvasView = new SKCanvasView();
        canvasView.PaintSurface += OnCanvasViewPaintSurface;
        Content = canvasView;
    }

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

        canvas.Clear();

        float x = (info.Width - bitmap.Width) / 2;
        float y = (info.Height - bitmap.Height) / 2;

        canvas.DrawBitmap(bitmap, x, y);
    }
}

Der PaintSurface Handler zentriert die Bitmap durch Berechnung x und y Werte basierend auf den Pixelabmessungen der Anzeigeoberfläche und den Pixelabmessungen der Bitmap:

Pixelabmessungen

Wenn die Anwendung die Bitmap in der oberen linken Ecke anzeigen möchte, würde sie einfach Koordinaten von (0, 0) übergeben.

Eine Methode zum Laden von Ressourcenbitmaps

Viele der verfügbaren Beispiele müssen Bitmapressourcen laden. Die statische BitmapExtensions Klasse in der Beispiellösung enthält eine Methode, die Ihnen helfen soll:

static class BitmapExtensions
{
    public static SKBitmap LoadBitmapResource(Type type, string resourceID)
    {
        Assembly assembly = type.GetTypeInfo().Assembly;

        using (Stream stream = assembly.GetManifestResourceStream(resourceID))
        {
            return SKBitmap.Decode(stream);
        }
    }
    ···
}

Beachten Sie den Type Parameter. Dies kann das Objekt sein, das Type einem beliebigen Typ in der Assembly zugeordnet ist, in dem die Bitmapressource gespeichert wird.

Diese LoadBitmapResource Methode wird in allen nachfolgenden Beispielen verwendet, die Bitmapressourcen erfordern.

Strecken zum Ausfüllen eines Rechtecks

Die SKCanvas Klasse definiert auch eine DrawBitmap Methode, die die Bitmap in einem Rechteck rendert, und eine andere DrawBitmap Methode, die eine rechteckige Teilmenge der Bitmap in einem Rechteck rendert:

DrawBitmap(SKBitmap bitmap, SKRect dest, SKPaint paint = null)

DrawBitmap(SKBitmap bitmap, SKRect source, SKRect dest, SKPaint paint = null)

In beiden Fällen wird die Bitmap gestreckt, um das Rechteck mit dem Namen zu destfüllen. In der zweiten Methode können Sie mit dem source Rechteck eine Teilmenge der Bitmap auswählen. Das dest Rechteck ist relativ zum Ausgabegerät; das source Rechteck ist relativ zur Bitmap.

Auf der Seite "Rechteck ausfüllen" wird die erste dieser beiden Methoden veranschaulicht, indem dieselbe Bitmap angezeigt wird, die im vorherigen Beispiel in einem Rechteck dieselbe Größe wie der Zeichenbereich verwendet wird:

public class FillRectanglePage : ContentPage
{
    SKBitmap bitmap =
        BitmapExtensions.LoadBitmapResource(typeof(FillRectanglePage),
                                            "SkiaSharpFormsDemos.Media.Banana.jpg");
    public FillRectanglePage ()
    {
        Title = "Fill Rectangle";

        SKCanvasView canvasView = new SKCanvasView();
        canvasView.PaintSurface += OnCanvasViewPaintSurface;
        Content = canvasView;
    }

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

        canvas.Clear();

        canvas.DrawBitmap(bitmap, info.Rect);
    }
}

Beachten Sie die Verwendung der neuen BitmapExtensions.LoadBitmapResource Methode zum Festlegen des SKBitmap Felds. Das Zielrechteck wird aus der Rect Eigenschaft von SKImageInfoabgerufen, die die Größe der Anzeigeoberfläche desribes:

Rechteck ausfüllen

Dies ist in der Regel nicht das, was Sie wollen. Das Bild wird verzerrt, indem es in horizontaler und vertikaler Richtung unterschiedlich gestreckt wird. Beim Anzeigen einer Bitmap in einer anderen Größe als der Pixelgröße möchten Sie in der Regel das ursprüngliche Seitenverhältnis der Bitmap beibehalten.

Strecken beim Beibehalten des Seitenverhältnisses

Das Strecken einer Bitmap beim Beibehalten des Seitenverhältnisses ist ein Prozess, der auch als einheitliche Skalierung bezeichnet wird. Dieser Begriff schlägt einen algorithmischen Ansatz vor. Eine mögliche Lösung wird auf der Seite "Uniform Scaling " angezeigt:

public class UniformScalingPage : ContentPage
{
    SKBitmap bitmap =
        BitmapExtensions.LoadBitmapResource(typeof(UniformScalingPage),
                                            "SkiaSharpFormsDemos.Media.Banana.jpg");
    public UniformScalingPage()
    {
        Title = "Uniform Scaling";

        SKCanvasView canvasView = new SKCanvasView();
        canvasView.PaintSurface += OnCanvasViewPaintSurface;
        Content = canvasView;
    }

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

        canvas.Clear();

        float scale = Math.Min((float)info.Width / bitmap.Width,
                               (float)info.Height / bitmap.Height);
        float x = (info.Width - scale * bitmap.Width) / 2;
        float y = (info.Height - scale * bitmap.Height) / 2;
        SKRect destRect = new SKRect(x, y, x + scale * bitmap.Width,
                                           y + scale * bitmap.Height);

        canvas.DrawBitmap(bitmap, destRect);
    }
}

Der PaintSurface Handler berechnet einen scale Faktor, der das Minimum des Verhältnisses der Anzeigebreite und -höhe zur Bitmapbreite und -höhe darstellt. Die x Werte y können dann für die Zentrierung der skalierten Bitmap innerhalb der Anzeigebreite und -höhe berechnet werden. Das Zielrechteck weist eine obere linke Ecke und xy eine untere rechte Ecke dieser Werte sowie die skalierte Breite und Höhe der Bitmap auf:

Einheitliche Skalierung

Drehen Sie das Telefon seitwärts, um die Bitmap zu diesem Bereich gestreckt anzuzeigen:

Einheitliche Skalierungslandschaft

Der Vorteil der Verwendung dieses scale Faktors wird offensichtlich, wenn Sie einen etwas anderen Algorithmus implementieren möchten. Angenommen, Sie möchten das Seitenverhältnis der Bitmap beibehalten, aber auch das Zielrechteck ausfüllen. Die einzige Möglichkeit besteht darin, einen Teil des Bilds zuzuschneiden, aber Sie können diesen Algorithmus einfach implementieren, indem Sie im obigen Code zu Math.Max wechselnMath.Min. Das Ergebnis lautet wie folgt:

Einheitliche Skalierungsalternative

Das Seitenverhältnis der Bitmap wird beibehalten, bereiche auf der linken und rechten Seite der Bitmap werden jedoch zugeschnitten.

Eine vielseitige Bitmapanzeigefunktion

XAML-basierte Programmierumgebungen (z. B. UWP und Xamarin.Forms) verfügen über eine Möglichkeit, die Größe von Bitmaps zu erweitern oder zu verkleinern und gleichzeitig ihre Seitenverhältnisse beizubehalten. Obwohl SkiaSharp dieses Feature nicht enthält, können Sie es selbst implementieren.

Die BitmapExtensions in der Beispielanwendung enthaltene Klasse zeigt, wie das geht. Die Klasse definiert zwei neue DrawBitmap Methoden, mit denen die Seitenverhältnisberechnung durchgeführt wird. Diese neuen Methoden sind Erweiterungsmethoden von SKCanvas.

Die neuen DrawBitmap Methoden enthalten einen Parameter vom Typ BitmapStretch, eine in der BitmapExtensions.cs Datei definierte Aufzählung:

public enum BitmapStretch
{
    None,
    Fill,
    Uniform,
    UniformToFill,
    AspectFit = Uniform,
    AspectFill = UniformToFill
}

Die NoneElemente , Fill, , Uniformund UniformToFill Member sind identisch mit denen in der UWP-Aufzählung Stretch . Die ähnliche Xamarin.FormsAspect Aufzählung definiert Member Fill, AspectFitund AspectFill.

Die oben gezeigte Seite für die einheitliche Skalierung zentriert die Bitmap innerhalb des Rechtecks, sie kann jedoch auch andere Optionen haben, z. B. das Positionieren der Bitmap auf der linken oder rechten Seite des Rechtecks oder oben oder unten. Dies ist der Zweck der BitmapAlignment Enumeration:

public enum BitmapAlignment
{
    Start,
    Center,
    End
}

Ausrichtungseinstellungen haben keine Auswirkung, wenn sie mit BitmapStretch.Fill.

Die erste DrawBitmap Erweiterungsfunktion enthält ein Zielrechteck, aber kein Quellrechteck. Standardwerte sind definiert, sodass Sie nur ein BitmapStretch Element angeben müssen, wenn die Bitmap zentriert werden soll:

static class BitmapExtensions
{
    ···
    public static void DrawBitmap(this SKCanvas canvas, SKBitmap bitmap, SKRect dest,
                                  BitmapStretch stretch,
                                  BitmapAlignment horizontal = BitmapAlignment.Center,
                                  BitmapAlignment vertical = BitmapAlignment.Center,
                                  SKPaint paint = null)
    {
        if (stretch == BitmapStretch.Fill)
        {
            canvas.DrawBitmap(bitmap, dest, paint);
        }
        else
        {
            float scale = 1;

            switch (stretch)
            {
                case BitmapStretch.None:
                    break;

                case BitmapStretch.Uniform:
                    scale = Math.Min(dest.Width / bitmap.Width, dest.Height / bitmap.Height);
                    break;

                case BitmapStretch.UniformToFill:
                    scale = Math.Max(dest.Width / bitmap.Width, dest.Height / bitmap.Height);
                    break;
            }

            SKRect display = CalculateDisplayRect(dest, scale * bitmap.Width, scale * bitmap.Height,
                                                  horizontal, vertical);

            canvas.DrawBitmap(bitmap, display, paint);
        }
    }
    ···
}

Der Hauptzweck dieser Methode besteht darin, einen Skalierungsfaktor scale zu berechnen, der dann beim Aufrufen der CalculateDisplayRect Methode auf die Bitmapbreite und -höhe angewendet wird. Dies ist die Methode, die ein Rechteck für die Anzeige der Bitmap basierend auf der horizontalen und vertikalen Ausrichtung berechnet:

static class BitmapExtensions
{
    ···
    static SKRect CalculateDisplayRect(SKRect dest, float bmpWidth, float bmpHeight,
                                       BitmapAlignment horizontal, BitmapAlignment vertical)
    {
        float x = 0;
        float y = 0;

        switch (horizontal)
        {
            case BitmapAlignment.Center:
                x = (dest.Width - bmpWidth) / 2;
                break;

            case BitmapAlignment.Start:
                break;

            case BitmapAlignment.End:
                x = dest.Width - bmpWidth;
                break;
        }

        switch (vertical)
        {
            case BitmapAlignment.Center:
                y = (dest.Height - bmpHeight) / 2;
                break;

            case BitmapAlignment.Start:
                break;

            case BitmapAlignment.End:
                y = dest.Height - bmpHeight;
                break;
        }

        x += dest.Left;
        y += dest.Top;

        return new SKRect(x, y, x + bmpWidth, y + bmpHeight);
    }
}

Die BitmapExtensions Klasse enthält eine zusätzliche DrawBitmap Methode mit einem Quellrechteck zum Angeben einer Teilmenge der Bitmap. Diese Methode ähnelt dem ersten, mit der Ausnahme, dass der Skalierungsfaktor basierend auf dem source Rechteck berechnet wird und dann im Aufruf auf das source Rechteck angewendet wird:CalculateDisplayRect

static class BitmapExtensions
{
    ···
    public static void DrawBitmap(this SKCanvas canvas, SKBitmap bitmap, SKRect source, SKRect dest,
                                  BitmapStretch stretch,
                                  BitmapAlignment horizontal = BitmapAlignment.Center,
                                  BitmapAlignment vertical = BitmapAlignment.Center,
                                  SKPaint paint = null)
    {
        if (stretch == BitmapStretch.Fill)
        {
            canvas.DrawBitmap(bitmap, source, dest, paint);
        }
        else
        {
            float scale = 1;

            switch (stretch)
            {
                case BitmapStretch.None:
                    break;

                case BitmapStretch.Uniform:
                    scale = Math.Min(dest.Width / source.Width, dest.Height / source.Height);
                    break;

                case BitmapStretch.UniformToFill:
                    scale = Math.Max(dest.Width / source.Width, dest.Height / source.Height);
                    break;
            }

            SKRect display = CalculateDisplayRect(dest, scale * source.Width, scale * source.Height,
                                                  horizontal, vertical);

            canvas.DrawBitmap(bitmap, source, display, paint);
        }
    }
    ···
}

Die erste dieser beiden neuen DrawBitmap Methoden wird auf der Seite "Skalierungsmodi" veranschaulicht. Die XAML-Datei enthält drei Picker Elemente, mit denen Sie Elemente und BitmapStretchBitmapAlignment Enumerationen auswählen können:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:SkiaSharpFormsDemos"
             xmlns:skia="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
             x:Class="SkiaSharpFormsDemos.Bitmaps.ScalingModesPage"
             Title="Scaling Modes">

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

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

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

        <Label Text="Stretch:"
               Grid.Row="1" Grid.Column="0"
               VerticalOptions="Center" />

        <Picker x:Name="stretchPicker"
                Grid.Row="1" Grid.Column="1"
                SelectedIndexChanged="OnPickerSelectedIndexChanged">
            <Picker.ItemsSource>
                <x:Array Type="{x:Type local:BitmapStretch}">
                    <x:Static Member="local:BitmapStretch.None" />
                    <x:Static Member="local:BitmapStretch.Fill" />
                    <x:Static Member="local:BitmapStretch.Uniform" />
                    <x:Static Member="local:BitmapStretch.UniformToFill" />
                </x:Array>
            </Picker.ItemsSource>

            <Picker.SelectedIndex>
                0
            </Picker.SelectedIndex>
        </Picker>

        <Label Text="Horizontal Alignment:"
               Grid.Row="2" Grid.Column="0"
               VerticalOptions="Center" />

        <Picker x:Name="horizontalPicker"
                Grid.Row="2" Grid.Column="1"
                SelectedIndexChanged="OnPickerSelectedIndexChanged">
            <Picker.ItemsSource>
                <x:Array Type="{x:Type local:BitmapAlignment}">
                    <x:Static Member="local:BitmapAlignment.Start" />
                    <x:Static Member="local:BitmapAlignment.Center" />
                    <x:Static Member="local:BitmapAlignment.End" />
                </x:Array>
            </Picker.ItemsSource>

            <Picker.SelectedIndex>
                0
            </Picker.SelectedIndex>
        </Picker>

        <Label Text="Vertical Alignment:"
               Grid.Row="3" Grid.Column="0"
               VerticalOptions="Center" />

        <Picker x:Name="verticalPicker"
                Grid.Row="3" Grid.Column="1"
                SelectedIndexChanged="OnPickerSelectedIndexChanged">
            <Picker.ItemsSource>
                <x:Array Type="{x:Type local:BitmapAlignment}">
                    <x:Static Member="local:BitmapAlignment.Start" />
                    <x:Static Member="local:BitmapAlignment.Center" />
                    <x:Static Member="local:BitmapAlignment.End" />
                </x:Array>
            </Picker.ItemsSource>

            <Picker.SelectedIndex>
                0
            </Picker.SelectedIndex>
        </Picker>
    </Grid>
</ContentPage>

Die CodeBehind-Datei macht einfach ungültig CanvasView , wenn ein Picker Element geändert wurde. Der PaintSurface Handler greift auf die drei Picker Ansichten zum Aufrufen der DrawBitmap Erweiterungsmethode zu:

public partial class ScalingModesPage : ContentPage
{
    SKBitmap bitmap =
        BitmapExtensions.LoadBitmapResource(typeof(ScalingModesPage),
                                            "SkiaSharpFormsDemos.Media.Banana.jpg");
    public ScalingModesPage()
    {
        InitializeComponent();
    }

    private void OnPickerSelectedIndexChanged(object sender, EventArgs args)
    {
        canvasView.InvalidateSurface();
    }

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

        canvas.Clear();

        SKRect dest = new SKRect(0, 0, info.Width, info.Height);

        BitmapStretch stretch = (BitmapStretch)stretchPicker.SelectedItem;
        BitmapAlignment horizontal = (BitmapAlignment)horizontalPicker.SelectedItem;
        BitmapAlignment vertical = (BitmapAlignment)verticalPicker.SelectedItem;

        canvas.DrawBitmap(bitmap, dest, stretch, horizontal, vertical);
    }
}

Hier sind einige Kombinationen von Optionen:

Skalierungsmodi

Die Seite " Rechteck-Teilmenge " hat praktisch dieselbe XAML-Datei wie skalierungsmodi, aber die CodeBehind-Datei definiert eine rechteckige Teilmenge der Bitmap, die vom SOURCE Feld angegeben wird:

public partial class ScalingModesPage : ContentPage
{
    SKBitmap bitmap =
        BitmapExtensions.LoadBitmapResource(typeof(ScalingModesPage),
                                            "SkiaSharpFormsDemos.Media.Banana.jpg");

    static readonly SKRect SOURCE = new SKRect(94, 12, 212, 118);

    public RectangleSubsetPage()
    {
        InitializeComponent();
    }

    private void OnPickerSelectedIndexChanged(object sender, EventArgs args)
    {
        canvasView.InvalidateSurface();
    }

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

        canvas.Clear();

        SKRect dest = new SKRect(0, 0, info.Width, info.Height);

        BitmapStretch stretch = (BitmapStretch)stretchPicker.SelectedItem;
        BitmapAlignment horizontal = (BitmapAlignment)horizontalPicker.SelectedItem;
        BitmapAlignment vertical = (BitmapAlignment)verticalPicker.SelectedItem;

        canvas.DrawBitmap(bitmap, SOURCE, dest, stretch, horizontal, vertical);
    }
}

Diese Rechteckquelle isoliert den Kopf des Affen, wie in den folgenden Screenshots gezeigt:

Rechteck-Teilmenge