Condividi tramite


Visualizzazione di bitmap SkiaSharp

L'oggetto delle bitmap SkiaSharp è stato introdotto nell'articolo Nozioni di base bitmap in SkiaSharp. Questo articolo ha illustrato tre modi per caricare bitmap e tre modi per visualizzare bitmap. Questo articolo esamina le tecniche per caricare le bitmap e illustra in modo più approfondito l'uso dei DrawBitmap metodi di SKCanvas.

Visualizzazione dell'esempio

I DrawBitmapLattice metodi e DrawBitmapNinePatch sono descritti nell'articolo Visualizzazione segmentata delle bitmap SkiaSharp.

Gli esempi in questa pagina provengono dall'applicazione di esempio. Nella home page dell'applicazione scegliere SkiaSharp Bitmap e quindi passare alla sezione Visualizzazione bitmap .

Caricamento di una bitmap

Una bitmap usata da un'applicazione SkiaSharp proviene in genere da una delle tre origini diverse:

  • Da Internet
  • Da una risorsa incorporata nell'eseguibile
  • Dalla raccolta foto dell'utente

È anche possibile che un'applicazione SkiaSharp crei una nuova bitmap e quindi disegnare su di essa o impostare i bit bitmap in modo algoritmico. Queste tecniche sono illustrate negli articoli Creazione e disegno su bitmap SkiaSharp e accesso ai pixel bitmap skiaSharp.

Nei tre esempi di codice seguenti di caricamento di una bitmap, si presuppone che la classe contenga un campo di tipo SKBitmap:

SKBitmap bitmap;

Come affermato dall'articolo Bitmap Basics in SkiaSharp, il modo migliore per caricare una bitmap su Internet è con la HttpClient classe . Una singola istanza della classe può essere definita come campo:

HttpClient httpClient = new HttpClient();

Quando si usa HttpClient con le applicazioni iOS e Android, è necessario impostare le proprietà del progetto come descritto nei documenti di Transport Layer Security (TLS) 1.2.

Il codice che usa HttpClient spesso comporta l'operatore await , pertanto deve risiedere in un async metodo:

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
{
    ···
}

Si noti che l'oggetto Stream ottenuto da GetStreamAsync viene copiato in un oggetto MemoryStream. Android non consente l'elaborazione dell'oggetto Stream from HttpClient dal thread principale, ad eccezione dei metodi asincroni.

Esegue SKBitmap.Decode molte operazioni: l'oggetto Stream passato fa riferimento a un blocco di memoria contenente un'intera bitmap in uno dei formati di file bitmap comuni, in genere JPEG, PNG o GIF. Il Decode metodo deve determinare il formato e quindi decodificare il file bitmap nel formato bitmap interno di SkiaSharp.

Dopo che il codice chiama SKBitmap.Decode, probabilmente invaliderà in CanvasView modo che il PaintSurface gestore possa visualizzare la bitmap appena caricata.

Il secondo modo per caricare una bitmap consiste nell'includere la bitmap come risorsa incorporata nella libreria .NET Standard a cui fanno riferimento i singoli progetti di piattaforma. Un ID risorsa viene passato al GetManifestResourceStream metodo . Questo ID risorsa è costituito dal nome dell'assembly, dal nome della cartella e dal nome file della risorsa separati da punti:

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

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

I file bitmap possono anche essere archiviati come risorse nel singolo progetto di piattaforma per iOS, Android e la piattaforma UWP (Universal Windows Platform) (UWP). Tuttavia, il caricamento di tali bitmap richiede codice che si trova nel progetto della piattaforma.

Un terzo approccio per ottenere una bitmap è dalla raccolta immagini dell'utente. Il codice seguente usa un servizio di dipendenza incluso nell'applicazione di esempio. La libreria SkiaSharpFormsDemo .NET Standard include l'interfaccia IPhotoLibrary , mentre ogni progetto di piattaforma contiene una PhotoLibrary classe che implementa tale interfaccia.

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

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

In genere, tale codice invalida anche in CanvasView modo che il PaintSurface gestore possa visualizzare la nuova bitmap.

La SKBitmap classe definisce diverse proprietà utili, tra cui Width e Height, che rivelano le dimensioni pixel della bitmap, nonché molti metodi, inclusi i metodi per creare bitmap, copiarli ed esporre i bit pixel.

Visualizzazione in dimensioni in pixel

La classe SkiaSharp Canvas definisce quattro DrawBitmap metodi. Questi metodi consentono la visualizzazione delle bitmap in due modi fondamentalmente diversi:

  • Se si specifica un SKPoint valore (o valori e y separatix) viene visualizzata la bitmap nelle dimensioni in pixel. I pixel della bitmap vengono mappati direttamente ai pixel della visualizzazione video.
  • Se si specifica un rettangolo, la bitmap viene estesa alle dimensioni e alla forma del rettangolo.

Viene visualizzata una bitmap nelle dimensioni in pixel usando DrawBitmap con un SKPoint parametro o DrawBitmap con parametri e y separatix:

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

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

Questi due metodi sono funzionalmente identici. Il punto specificato indica la posizione dell'angolo superiore sinistro della bitmap rispetto all'area di disegno. Poiché la risoluzione dei pixel dei dispositivi mobili è così elevata, le bitmap più piccole in genere appaiono abbastanza piccole su questi dispositivi.

Il parametro facoltativo SKPaint consente di visualizzare la bitmap usando la trasparenza. A tale scopo, creare un SKPaint oggetto e impostare la Color proprietà su qualsiasi SKColor valore con un canale alfa minore di 1. Ad esempio:

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

Il 0x80 passato come ultimo argomento indica la trasparenza del 50%. È anche possibile impostare un canale alfa su uno dei colori predefiniti:

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

Tuttavia, il colore stesso è irrilevante. Solo il canale alfa viene esaminato quando si utilizza l'oggetto SKPaint in una DrawBitmap chiamata.

L'oggetto SKPaint svolge anche un ruolo durante la visualizzazione di bitmap tramite modalità di fusione o effetti di filtro. Questi sono illustrati negli articoli SkiaSharp compositing and blend modes and SkiaSharp image filters (Modalità di composizione e fusione skiaSharp e filtri immagine SkiaSharp).

Nella pagina Dimensioni pixel del programma di esempio viene visualizzata una risorsa bitmap larga 320 pixel di 240 pixel:

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

Il PaintSurface gestore centra la bitmap calcolando e y i x valori in base alle dimensioni pixel della superficie di visualizzazione e alle dimensioni pixel della bitmap:

Dimensioni pixel

Se l'applicazione desidera visualizzare la bitmap nell'angolo superiore sinistro, passerebbe semplicemente le coordinate di (0, 0).

Metodo per il caricamento di bitmap di risorse

Molti degli esempi in arrivo dovranno caricare le risorse bitmap. La classe statica BitmapExtensions nella soluzione di esempio contiene un metodo per aiutare:

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

Si noti il Type parametro . Può trattarsi dell'oggetto Type associato a qualsiasi tipo nell'assembly in cui è archiviata la risorsa bitmap.

Questo LoadBitmapResource metodo verrà usato in tutti gli esempi successivi che richiedono risorse bitmap.

Estensione per riempire un rettangolo

La SKCanvas classe definisce anche un DrawBitmap metodo che esegue il rendering della bitmap in un rettangolo e un altro DrawBitmap metodo che esegue il rendering di un subset rettangolare della bitmap in un rettangolo:

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

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

In entrambi i casi, la bitmap viene estesa per riempire il rettangolo denominato dest. Nel secondo metodo, il source rettangolo consente di selezionare un subset della bitmap. Il rettangolo è relativo al dispositivo di output. Il dest source rettangolo è relativo alla bitmap.

La pagina Riempimento rettangolo illustra il primo di questi due metodi visualizzando la stessa bitmap usata nell'esempio precedente in un rettangolo con le stesse dimensioni dell'area di disegno:

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

Si noti l'uso del nuovo BitmapExtensions.LoadBitmapResource metodo per impostare il SKBitmap campo. Il rettangolo di destinazione viene ottenuto dalla Rect proprietà di SKImageInfo, che desribizza le dimensioni della superficie di visualizzazione:

Riempimento rettangolo

Questo di solito non è quello che vuoi. L'immagine viene distorta in modo diverso nelle direzioni orizzontali e verticali. Quando si visualizza una bitmap in un elemento diverso dalle dimensioni del pixel, in genere si desidera mantenere le proporzioni originali della bitmap.

Stretching mantenendo le proporzioni

L'estensione di una bitmap mantenendo le proporzioni è un processo noto anche come scalabilità uniforme. Questo termine suggerisce un approccio algoritmico. Una possibile soluzione è visualizzata nella pagina Scalabilità uniforme:

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

Il PaintSurface gestore calcola un scale fattore che rappresenta il minimo del rapporto tra la larghezza e l'altezza della visualizzazione alla larghezza e all'altezza della bitmap. I x valori e y possono quindi essere calcolati per allineare al centro la bitmap ridimensionata all'interno della larghezza e dell'altezza dello schermo. Il rettangolo di destinazione ha un angolo superiore sinistro di x e y un angolo inferiore destro di tali valori, oltre alla larghezza e all'altezza ridimensionate della bitmap:

Scalabilità uniforme

Girare il telefono lateralmente per visualizzare la bitmap estesa a tale area:

Orizzontale di scalabilità uniforme

Il vantaggio dell'uso di questo scale fattore diventa ovvio quando si vuole implementare un algoritmo leggermente diverso. Si supponga di voler mantenere le proporzioni della bitmap, ma anche di riempire il rettangolo di destinazione. L'unico modo possibile è ritagliare parte dell'immagine, ma è possibile implementare tale algoritmo semplicemente modificando Math.Min in Math.Max nel codice precedente. Il risultato è il seguente:

Alternativa al ridimensionamento uniforme

Le proporzioni della bitmap vengono mantenute, ma le aree a sinistra e a destra della bitmap vengono ritagliate.

Una funzione di visualizzazione bitmap versatile

Gli ambienti di programmazione basati su XAML (ad esempio UWP e Xamarin.Forms) hanno una funzionalità per espandere o ridurre le dimensioni delle bitmap mantenendo le proporzioni. Anche se SkiaSharp non include questa funzionalità, è possibile implementarla manualmente.

La BitmapExtensions classe inclusa nell'applicazione di esempio mostra come. La classe definisce due nuovi DrawBitmap metodi che eseguono il calcolo delle proporzioni. Questi nuovi metodi sono metodi di estensione di SKCanvas.

I nuovi DrawBitmap metodi includono un parametro di tipo BitmapStretch, un'enumerazione definita nel file BitmapExtensions.cs :

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

I Nonemembri , Fill, Uniforme UniformToFill sono uguali a quelli nell'enumerazione UWP Stretch . L'enumerazione simile Xamarin.FormsAspect definisce i membri Fill, AspectFite AspectFill.

La pagina Scalabilità uniforme mostrata sopra centra la bitmap all'interno del rettangolo, ma è possibile che si vogliano altre opzioni, ad esempio posizionare la bitmap sul lato sinistro o destro del rettangolo o superiore o inferiore. Questo è lo scopo dell'enumerazione BitmapAlignment :

public enum BitmapAlignment
{
    Start,
    Center,
    End
}

Le impostazioni di allineamento non hanno alcun effetto se usate con BitmapStretch.Fill.

La prima DrawBitmap funzione di estensione contiene un rettangolo di destinazione, ma nessun rettangolo di origine. Le impostazioni predefinite sono definite in modo che, se si desidera che la bitmap sia allineata al centro, è necessario specificare solo un BitmapStretch membro:

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

Lo scopo principale di questo metodo è calcolare un fattore di ridimensionamento denominato scale che viene quindi applicato alla larghezza e all'altezza della bitmap quando si chiama il CalculateDisplayRect metodo . Questo è il metodo che calcola un rettangolo per la visualizzazione della bitmap in base all'allineamento orizzontale e verticale:

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

La BitmapExtensions classe contiene un metodo aggiuntivo DrawBitmap con un rettangolo di origine per specificare un subset della bitmap. Questo metodo è simile al primo, ad eccezione del fatto che il fattore di ridimensionamento viene calcolato in base al source rettangolo e quindi applicato al source rettangolo nella chiamata a 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);
        }
    }
    ···
}

Il primo di questi due nuovi DrawBitmap metodi è illustrato nella pagina Modalità di ridimensionamento. Il file XAML contiene tre Picker elementi che consentono di selezionare i membri delle BitmapStretch BitmapAlignment e enumerazioni:

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

Il file code-behind invalida semplicemente quando CanvasView un Picker elemento è stato modificato. Il PaintSurface gestore accede alle tre Picker visualizzazioni per chiamare il DrawBitmap metodo di estensione:

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

Ecco alcune combinazioni di opzioni:

Modalità di ridimensionamento

La pagina Rettangolo Subset ha praticamente lo stesso file XAML delle modalità di ridimensionamento, ma il file code-behind definisce un subset rettangolare della bitmap specificata dal SOURCE campo:

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

Questa origine rettangolo isola la testa della scimmia, come illustrato in questi screenshot:

Sottoinsieme rettangolo