Partager via


Affichage des bitmaps SkiaSharp

L’objet des bitmaps SkiaSharp a été introduit dans l’article Bitmap Basics in SkiaSharp. Cet article a montré trois façons de charger des bitmaps et trois façons d’afficher des bitmaps. Cet article passe en revue les techniques de chargement des bitmaps et approfondit l’utilisation des DrawBitmap méthodes de SKCanvas.

Affichage de l’exemple

Les DrawBitmapLattice méthodes et DrawBitmapNinePatch les méthodes sont décrites dans l’article Affichage segmenté des bitmaps SkiaSharp.

Les exemples de cette page proviennent de l’exemple d’application. Dans la page d’accueil de cette application, choisissez Bitmaps SkiaSharp, puis accédez à la section Affichage des bitmaps .

Chargement d’une bitmap

Une bitmap utilisée par une application SkiaSharp provient généralement d’une des trois sources différentes :

  • À partir d’Internet
  • À partir d’une ressource incorporée dans l’exécutable
  • À partir de la bibliothèque de photos de l’utilisateur

Il est également possible pour une application SkiaSharp de créer une image bitmap, puis de dessiner dessus ou de définir les bits bitmap de manière algorithmique. Ces techniques sont abordées dans les articles Création et dessin sur les bitmaps SkiaSharp et l’accès aux pixels bitmap SkiaSharp.

Dans les trois exemples de code suivants de chargement d’une bitmap, la classe est supposée contenir un champ de type SKBitmap:

SKBitmap bitmap;

Comme indiqué dans l’article Bitmap Basics in SkiaSharp, la meilleure façon de charger une bitmap sur Internet est avec la HttpClient classe. Une seule instance de la classe peut être définie en tant que champ :

HttpClient httpClient = new HttpClient();

Lorsque vous utilisez HttpClient des applications iOS et Android, vous devez définir les propriétés du projet comme décrit dans les documents sur TLS (Transport Layer Security) 1.2.

Le code qui utilise HttpClient souvent implique l’opérateur await . Il doit donc résider dans une async méthode :

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

Notez que l’objet Stream obtenu GetStreamAsync est copié dans un MemoryStream. Android n’autorise pas le Stream traitement par HttpClient le thread principal, sauf dans les méthodes asynchrones.

Le SKBitmap.Decode fait beaucoup de travail : l’objet Stream passé à celui-ci fait référence à un bloc de mémoire contenant une image bitmap entière dans l’un des formats de fichier bitmap courants, généralement JPEG, PNG ou GIF. La Decode méthode doit déterminer le format, puis décoder le fichier bitmap dans le propre format bitmap interne de SkiaSharp.

Une fois que votre code a appelé SKBitmap.Decode, il invalidera probablement le CanvasView gestionnaire afin que le PaintSurface gestionnaire puisse afficher l’image bitmap nouvellement chargée.

La deuxième façon de charger une bitmap consiste à inclure la bitmap en tant que ressource incorporée dans la bibliothèque .NET Standard référencée par les projets de plateforme individuels. Un ID de ressource est passé à la GetManifestResourceStream méthode. Cet ID de ressource se compose du nom de l’assembly, du nom du dossier et du nom de fichier de la ressource séparés par des points :

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

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

Les fichiers bitmap peuvent également être stockés en tant que ressources dans le projet de plateforme individuelle pour iOS, Android et le plateforme Windows universelle (UWP). Toutefois, le chargement de ces bitmaps nécessite du code qui se trouve dans le projet de plateforme.

Une troisième approche pour obtenir une bitmap provient de la bibliothèque d’images de l’utilisateur. Le code suivant utilise un service de dépendance inclus dans l’exemple d’application. La bibliothèque SkiaSharpFormsDemo .NET Standard inclut l’interface IPhotoLibrary , tandis que chacun des projets de plateforme contient une PhotoLibrary classe qui implémente cette interface.

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

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

En règle générale, ce code invalide également la CanvasView façon dont le PaintSurface gestionnaire peut afficher la nouvelle bitmap.

La SKBitmap classe définit plusieurs propriétés utiles, notamment Width et Height, qui révèlent les dimensions de pixels de la bitmap, ainsi que de nombreuses méthodes, y compris les méthodes permettant de créer des bitmaps, de les copier et d’exposer les bits de pixels.

Affichage en dimensions de pixels

La classe SkiaSharp Canvas définit quatre DrawBitmap méthodes. Ces méthodes permettent aux bitmaps d’être affichées de deux façons fondamentalement différentes :

  • La spécification d’une SKPoint valeur (ou de valeurs distinctes x ) y affiche la bitmap dans ses dimensions de pixels. Les pixels de la bitmap sont mappés directement aux pixels de l’affichage vidéo.
  • La spécification d’un rectangle entraîne l’étirement de la bitmap à la taille et à la forme du rectangle.

Vous affichez une bitmap dans ses dimensions de pixels à l’aide DrawBitmap d’un SKPoint paramètre ou DrawBitmap avec des paramètres distincts et y des x paramètres :

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

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

Ces deux méthodes sont fonctionnellement identiques. Le point spécifié indique l’emplacement du coin supérieur gauche de la bitmap par rapport au canevas. Étant donné que la résolution de pixels des appareils mobiles est si élevée, les bitmaps plus petites apparaissent généralement assez minuscules sur ces appareils.

Le paramètre facultatif SKPaint vous permet d’afficher la bitmap à l’aide de la transparence. Pour ce faire, créez un SKPaint objet et définissez la Color propriété sur n’importe quelle SKColor valeur avec un canal alpha inférieur à 1. Par exemple :

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

Le 0x80 passé comme dernier argument indique une transparence de 50 %. Vous pouvez également définir un canal alpha sur l’une des couleurs prédéfinies :

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

Toutefois, la couleur elle-même n’est pas pertinente. Seul le canal alpha est examiné lorsque vous utilisez l’objet SKPaint dans un DrawBitmap appel.

L’objet SKPaint joue également un rôle lors de l’affichage de bitmaps à l’aide de modes de fusion ou d’effets de filtre. Ils sont présentés dans les articles SkiaSharp compositing et blend modes et filtres d’images SkiaSharp.

La page Dimensions de pixels de l’exemple de programme affiche une ressource bitmap de 320 pixels de large de 240 pixels :

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

Le PaintSurface gestionnaire centre la bitmap en calculant x et y en fonction des valeurs en fonction des dimensions de pixels de l’aire d’affichage et des dimensions de pixels de la bitmap :

Dimensions de pixels

Si l’application souhaite afficher la bitmap dans son coin supérieur gauche, elle passe simplement les coordonnées de (0, 0).

Méthode de chargement des bitmaps de ressources

La plupart des exemples à venir devront charger des ressources bitmap. La classe statique BitmapExtensions dans l’exemple de solution contient une méthode pour vous aider :

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

Notez le Type paramètre. Il peut s’agir de l’objet Type associé à n’importe quel type dans l’assembly qui stocke la ressource bitmap.

Cette LoadBitmapResource méthode sera utilisée dans tous les exemples suivants qui nécessitent des ressources bitmap.

Étirement pour remplir un rectangle

La SKCanvas classe définit également une DrawBitmap méthode qui restitue la bitmap dans un rectangle et une autre DrawBitmap méthode qui restitue un sous-ensemble rectangulaire de la bitmap dans un rectangle :

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

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

Dans les deux cas, la bitmap est étirée pour remplir le rectangle nommé dest. Dans la deuxième méthode, le source rectangle vous permet de sélectionner un sous-ensemble de la bitmap. Le dest rectangle est relatif à l’appareil de sortie ; le source rectangle est relatif à la bitmap.

La page Remplissage rectangle illustre la première de ces deux méthodes en affichant la même bitmap utilisée dans l’exemple précédent dans un rectangle de la même taille que le canevas :

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

Notez l’utilisation de la nouvelle BitmapExtensions.LoadBitmapResource méthode pour définir le SKBitmap champ. Le rectangle de destination est obtenu à partir de la Rect propriété de SKImageInfo, qui déséribe la taille de l’aire d’affichage :

Rectangle de remplissage

Ce n’est généralement pas ce que vous voulez. L’image est déformée en étant étirée différemment dans les directions horizontales et verticales. Lors de l’affichage d’une bitmap dans une autre taille que sa taille de pixel, vous souhaitez généralement conserver le rapport d’aspect d’origine de la bitmap.

Étirement tout en préservant les proportions

L’étirement d’une bitmap tout en préservant le rapport d’aspect est un processus également appelé mise à l’échelle uniforme. Ce terme suggère une approche algorithmique. Une solution possible s’affiche dans la page Mise à l’échelle 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);
    }
}

Le PaintSurface gestionnaire calcule un scale facteur qui correspond au minimum au ratio de la largeur d’affichage et de la hauteur à la largeur et à la hauteur bitmap. Les x valeurs y et les valeurs peuvent ensuite être calculées pour centrer la bitmap mise à l’échelle dans la largeur et la hauteur d’affichage. Le rectangle de destination comporte un angle supérieur gauche et xy un coin inférieur droit de ces valeurs, ainsi que la largeur et la hauteur mises à l’échelle de la bitmap :

Mise à l’échelle uniforme

Tournez le téléphone de côté pour voir la bitmap étirée vers cette zone :

Paysage de mise à l’échelle uniforme

L’avantage de l’utilisation de ce scale facteur devient évident lorsque vous souhaitez implémenter un algorithme légèrement différent. Supposons que vous souhaitez conserver le rapport d’aspect de la bitmap, mais également remplir le rectangle de destination. La seule façon possible est de rogner une partie de l’image, mais vous pouvez implémenter cet algorithme simplement en passant Math.Min au Math.Max code ci-dessus. Voici le résultat :

Alternative à la mise à l’échelle uniforme

Le rapport d’aspect de la bitmap est conservé, mais les zones à gauche et à droite de la bitmap sont rognées.

Fonction d’affichage bitmap polyvalente

Les environnements de programmation XAML (tels que UWP et Xamarin.Forms) disposent d’une fonctionnalité permettant d’étendre ou de réduire la taille des bitmaps tout en préservant leurs proportions. Bien que SkiaSharp n’inclut pas cette fonctionnalité, vous pouvez l’implémenter vous-même.

La BitmapExtensions classe incluse dans l’exemple d’application montre comment procéder. La classe définit deux nouvelles DrawBitmap méthodes qui effectuent le calcul des proportions. Ces nouvelles méthodes sont des méthodes d’extension de SKCanvas.

Les nouvelles DrawBitmap méthodes incluent un paramètre de type BitmapStretch, une énumération définie dans le fichier BitmapExtensions.cs :

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

Les Nonemembres , et UniformUniformToFill les membres Fillsont identiques à ceux de l’énumération UWPStretch. L’énumération similaire Xamarin.FormsAspect définit les membres Fill, AspectFitet AspectFill.

La page Mise à l’échelle uniforme affichée ci-dessus centre la bitmap dans le rectangle, mais vous pouvez souhaiter d’autres options, telles que le positionnement de la bitmap à gauche ou à droite du rectangle, ou le haut ou le bas. C’est l’objectif de l’énumération BitmapAlignment :

public enum BitmapAlignment
{
    Start,
    Center,
    End
}

Les paramètres d’alignement n’ont aucun effet lorsqu’ils sont utilisés avec BitmapStretch.Fill.

La première DrawBitmap fonction d’extension contient un rectangle de destination, mais aucun rectangle source. Les valeurs par défaut sont définies de sorte que si vous souhaitez que l’image bitmap soit centrée, vous n’avez besoin que de spécifier un BitmapStretch membre :

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

L’objectif principal de cette méthode est de calculer un facteur de mise à l’échelle nommé scale qui est ensuite appliqué à la largeur et à la hauteur bitmap lors de l’appel de la CalculateDisplayRect méthode. Il s’agit de la méthode qui calcule un rectangle pour afficher la bitmap en fonction de l’alignement horizontal et vertical :

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 contient une méthode supplémentaire DrawBitmap avec un rectangle source pour spécifier un sous-ensemble de la bitmap. Cette méthode est similaire à la première, sauf que le facteur de mise à l’échelle est calculé en fonction du source rectangle, puis appliqué au source rectangle dans l’appel à 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);
        }
    }
    ···
}

La première de ces deux nouvelles DrawBitmap méthodes est illustrée dans la page Modes de mise à l’échelle. Le fichier XAML contient trois Picker éléments qui vous permettent de sélectionner des membres des BitmapStretch éléments et BitmapAlignment énumérations :

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

Le fichier code-behind invalide simplement le CanvasView moment où un Picker élément a changé. Le PaintSurface gestionnaire accède aux trois Picker vues pour appeler la méthode d’extension DrawBitmap :

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

Voici quelques combinaisons d’options :

Modes de mise à l’échelle

La page Sous-ensemble de rectangles comporte pratiquement le même fichier XAML que les modes de mise à l’échelle, mais le fichier code-behind définit un sous-ensemble rectangulaire de la bitmap donnée par le SOURCE champ :

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

Cette source de rectangle isole la tête du singe, comme illustré dans ces captures d’écran :

Sous-ensemble de rectangles