Udostępnij za pośrednictwem


Wyświetlanie map bitowych SkiaSharp

Temat map bitowych SkiaSharp został wprowadzony w artykule Podstawy mapy bitowej w SkiaSharp. W tym artykule przedstawiono trzy sposoby ładowania map bitowych i trzy sposoby wyświetlania map bitowych. W tym artykule przedstawiono techniki ładowania map bitowych i bardziej szczegółowe informacje na temat DrawBitmap używania metod programu SKCanvas.

Wyświetlanie przykładu

Metody DrawBitmapLattice i DrawBitmapNinePatch zostały omówione w artykule Segmented display of SkiaSharp bitmaps (Segmented display of SkiaSharp map bitowych).

Przykłady na tej stronie pochodzą z przykładowej aplikacji. Na stronie głównej tej aplikacji wybierz pozycję Mapy bitowe SkiaSharp, a następnie przejdź do sekcji Wyświetlanie map bitowych.

Ładowanie mapy bitowej

Mapa bitowa używana przez aplikację SkiaSharp zazwyczaj pochodzi z jednego z trzech różnych źródeł:

  • Z Internetu
  • Z zasobu osadzonego w pliku wykonywalnym
  • Z biblioteki zdjęć użytkownika

Istnieje również możliwość utworzenia nowej mapy bitowej przez aplikację SkiaSharp, a następnie narysować ją lub ustawić bity mapy bitowej algorytmicznie. Te techniki zostały omówione w artykułach Tworzenie i rysowanie na mapach bitowych SkiaSharp oraz Uzyskiwanie dostępu do pikseli mapy bitowej SkiaSharp.

W poniższych trzech przykładach kodu ładowania mapy bitowej przyjmuje się, że klasa zawiera pole typu SKBitmap:

SKBitmap bitmap;

Jak stwierdził artykuł Bitmap Basics in SkiaSharp , najlepszym sposobem ładowania mapy bitowej przez Internet jest HttpClient klasa . Pojedyncze wystąpienie klasy można zdefiniować jako pole:

HttpClient httpClient = new HttpClient();

W przypadku korzystania HttpClient z aplikacji systemu iOS i Android należy ustawić właściwości projektu zgodnie z opisem w dokumentach protokołu Transport Layer Security (TLS) 1.2.

Kod, który używa HttpClient operatora często obejmuje await operator, więc musi znajdować się w metodzie async :

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

Zwróć uwagę, że Stream obiekt uzyskany z GetStreamAsync obiektu jest kopiowany do obiektu MemoryStream. System Android nie zezwala na Stream przetwarzanie elementu z HttpClient przez główny wątek z wyjątkiem metod asynchronicznych.

Wykonuje SKBitmap.Decode wiele pracy: Stream Obiekt przekazany do niego odwołuje się do bloku pamięci zawierającej całą mapę bitową w jednym z typowych formatów plików bitowych, na ogół JPEG, PNG lub GIF. Metoda Decode musi określić format, a następnie zdekodować plik mapy bitowej do wewnętrznego formatu mapy bitowej SkiaSharp.

Po wywołaniu SKBitmap.Decodekodu prawdopodobnie unieważni CanvasView element tak, aby PaintSurface program obsługi mógł wyświetlić nowo załadowaną mapę bitową.

Drugim sposobem ładowania mapy bitowej jest dołączenie mapy bitowej jako zasobu osadzonego w bibliotece .NET Standard, do których odwołuje się poszczególne projekty platformy. Identyfikator zasobu jest przekazywany do GetManifestResourceStream metody . Ten identyfikator zasobu składa się z nazwy zestawu, nazwy folderu i nazwy pliku zasobu oddzielonego kropkami:

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

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

Pliki mapy bitowej mogą być również przechowywane jako zasoby w indywidualnym projekcie platformy dla systemów iOS, Android i platforma uniwersalna systemu Windows (UWP). Jednak ładowanie tych map bitowych wymaga kodu znajdującego się w projekcie platformy.

Trzecim podejściem do uzyskania mapy bitowej jest biblioteka obrazów użytkownika. Poniższy kod używa usługi zależności uwzględnionej w przykładowej aplikacji. Biblioteka SkiaSharpFormsDemo .NET Standard Library zawiera IPhotoLibrary interfejs, podczas gdy każdy z projektów platformy zawiera klasę PhotoLibrary , która implementuje ten interfejs.

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

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

Ogólnie rzecz biorąc, taki kod unieważnia CanvasView również element tak, aby PaintSurface program obsługi mógł wyświetlić nową mapę bitową.

Klasa SKBitmap definiuje kilka przydatnych właściwości, w tym Width i Height, które ujawniają wymiary pikseli mapy bitowej, a także wiele metod, w tym metod tworzenia map bitów bitowych, kopiowania ich i uwidocznienia bitów pikseli.

Wyświetlanie w wymiarach pikseli

Klasa SkiaSharp Canvas definiuje cztery DrawBitmap metody. Te metody umożliwiają wyświetlanie map bitowych na dwa zasadniczo różne sposoby:

  • Określenie SKPoint wartości (lub oddzielnych x i y wartości) powoduje wyświetlenie mapy bitowej w wymiarach pikseli. Piksele mapy bitowej są mapowane bezpośrednio na piksele ekranu wideo.
  • Określenie prostokąta powoduje rozciągnięcie mapy bitowej na rozmiar i kształt prostokąta.

Mapa bitowa jest wyświetlana w wymiarach pikseli przy użyciu DrawBitmap parametru SKPoint lub DrawBitmap z oddzielnymi x parametrami i y :

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

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

Te dwie metody są funkcjonalnie identyczne. Określony punkt wskazuje lokalizację lewego górnego rogu mapy bitowej względem kanwy. Ponieważ rozdzielczość pikseli urządzeń przenośnych jest tak wysoka, mniejsze mapy bitowe zwykle wydają się dość małe na tych urządzeniach.

Opcjonalny SKPaint parametr umożliwia wyświetlanie mapy bitowej przy użyciu przezroczystości. W tym celu utwórz SKPaint obiekt i ustaw Color właściwość na dowolną SKColor wartość z kanałem alfa mniejszym niż 1. Na przykład:

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

0x80 przekazana jako ostatni argument wskazuje 50% przezroczystości. Kanał alfa można również ustawić na jednym ze wstępnie zdefiniowanych kolorów:

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

Jednak sam kolor jest nieistotny. Podczas używania SKPaint obiektu w wywołaniu DrawBitmap sprawdzany jest tylko kanał alfa.

Obiekt SKPaint odgrywa również rolę podczas wyświetlania map bitowych przy użyciu trybów mieszania lub efektów filtrowania. Przedstawiono je w artykułach SkiaSharp kompositing i blend tryby oraz filtry obrazów SkiaSharp.

Strona Wymiary pikseli w przykładowym programie wyświetla zasób mapy bitowej o szerokości 320 pikseli o wysokości 240 pikseli:

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

Procedura PaintSurface obsługi wyśrodkuje mapę bitową, obliczając x i y wartości na podstawie wymiarów pikseli powierzchni wyświetlania i wymiarów pikseli mapy bitowej:

Pikselach

Jeśli aplikacja chce wyświetlić mapę bitową w lewym górnym rogu, po prostu przekaże współrzędne (0, 0).

Metoda ładowania map bitowych zasobów

Wiele z nadchodzących próbek będzie musiało załadować zasoby mapy bitowej. Klasa statyczna BitmapExtensions w przykładowym rozwiązaniu zawiera metodę pomagającą:

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

Zwróć uwagę na Type parametr . Może to być Type obiekt skojarzony z dowolnym typem w zestawie, który przechowuje zasób mapy bitowej.

Ta LoadBitmapResource metoda będzie używana we wszystkich kolejnych próbkach, które wymagają zasobów mapy bitowej.

Rozciąganie w celu wypełnienia prostokąta

Klasa SKCanvas definiuje również metodę DrawBitmap , która renderuje mapę bitową do prostokąta, oraz inną DrawBitmap metodę, która renderuje prostokątny podzbiór mapy bitowej do prostokąta:

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

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

W obu przypadkach mapa bitowa jest rozciągana w celu wypełnienia prostokąta o nazwie dest. W drugiej metodzie source prostokąt umożliwia wybranie podzbioru mapy bitowej. Prostokąt dest jest względny względem urządzenia wyjściowego; source prostokąt jest względny względem mapy bitowej.

Strona Wypełnienie prostokąta przedstawia pierwszą z tych dwóch metod, wyświetlając tę samą mapę bitową używaną we wcześniejszym przykładzie w prostokątze o takim samym rozmiarze jak kanwa:

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

Zwróć uwagę na użycie nowej BitmapExtensions.LoadBitmapResource metody do ustawienia SKBitmap pola. Prostokąt docelowy jest uzyskiwany z Rect właściwości SKImageInfo, która rozdziela rozmiar powierzchni wyświetlania:

Wypełnienie prostokąta

Zwykle nie jest to to, co chcesz. Obraz jest zniekształcony przez rozciągnięcie w różnych kierunkach poziomych i pionowych. Podczas wyświetlania mapy bitowej w innym rozmiarze niż rozmiar piksela zwykle chcesz zachować oryginalny współczynnik proporcji mapy bitowej.

Rozciąganie przy zachowaniu współczynnika proporcji

Rozciąganie mapy bitowej przy zachowaniu współczynnika proporcji jest procesem znanym również jako jednolite skalowanie. Ten termin sugeruje podejście algorytmiczne. Jedno z możliwych rozwiązań jest wyświetlane na stronie Ujednolicone skalowanie :

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

Procedura PaintSurface obsługi oblicza scale współczynnik, który jest minimalnym współczynnikiem szerokości i wysokości ekranu do szerokości i wysokości mapy bitowej. Wartości x i y można następnie obliczyć na potrzeby wyśrodkowania skalowanych map bitowych w obrębie szerokości i wysokości ekranu. Prostokąt docelowy ma lewy górny róg x i y prawy dolny róg tych wartości oraz skalowaną szerokość i wysokość mapy bitowej:

Jednolite skalowanie

Przesuń telefon w bok, aby zobaczyć mapę bitową rozciągniętą do tego obszaru:

Jednolite skalowanie poziome

Zaleta korzystania z tego scale czynnika staje się oczywista, gdy chcesz zaimplementować nieco inny algorytm. Załóżmy, że chcesz zachować współczynnik proporcji mapy bitowej, ale także wypełnić prostokąt docelowy. Jedynym sposobem, w jaki jest to możliwe, jest przycinanie części obrazu, ale można zaimplementować ten algorytm po prostu zmieniając Math.Min wartość na Math.Max w powyższym kodzie. Oto wynik:

Alternatywa jednolitego skalowania

Współczynnik proporcji mapy bitowej jest zachowywany, ale obszary po lewej i prawej stronie mapy bitowej są przycinane.

Wszechstronna funkcja wyświetlania map bitowych

Środowiska programowania oparte na języku XAML (takie jak platforma UWP i Xamarin.Forms) mają obiekt umożliwiający rozszerzanie lub zmniejszanie rozmiaru map bitowych przy zachowaniu współczynników proporcji. Mimo że skiaSharp nie obejmuje tej funkcji, możesz zaimplementować ją samodzielnie.

Klasa BitmapExtensions uwzględniona w przykładowej aplikacji pokazuje, jak to zrobić. Klasa definiuje dwie nowe DrawBitmap metody, które wykonują obliczenia współczynnika proporcji. Te nowe metody to metody SKCanvasrozszerzenia klasy .

Nowe DrawBitmap metody obejmują parametr typu BitmapStretch, wyliczenie zdefiniowane w pliku BitmapExtensions.cs :

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

Elementy Noneczłonkowskie , Fill, Uniformi UniformToFill są takie same jak w wyliczenie platformy UWP Stretch . Xamarin.FormsAspect Podobne wyliczenie definiuje elementy członkowskie Fill, AspectFiti AspectFill.

Strona Jednolite skalowanie pokazana powyżej wyśrodkuje mapę bitową w obrębie prostokąta, ale możesz chcieć wybrać inne opcje, takie jak pozycjonowanie mapy bitowej po lewej lub prawej stronie prostokąta albo u góry lub dołu. Jest to cel BitmapAlignment wyliczenia:

public enum BitmapAlignment
{
    Start,
    Center,
    End
}

Ustawienia wyrównania nie mają żadnego wpływu w przypadku użycia z BitmapStretch.Fillprogramem .

Pierwsza DrawBitmap funkcja rozszerzenia zawiera prostokąt docelowy, ale bez prostokąta źródłowego. Wartości domyślne są definiowane tak, aby jeśli chcesz wyśrodkować mapę bitową, musisz określić tylko element członkowski BitmapStretch :

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

Podstawowym celem tej metody jest obliczenie współczynnika skalowania o nazwie scale , który jest następnie stosowany do szerokości i wysokości mapy bitowej podczas wywoływania CalculateDisplayRect metody. Jest to metoda, która oblicza prostokąt do wyświetlania mapy bitowej na podstawie wyrównania poziomego i pionowego:

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

Klasa BitmapExtensions zawiera dodatkową DrawBitmap metodę z prostokątem źródłowym określającym podzbiór mapy bitowej. Ta metoda jest podobna do pierwszej z tą różnicą, że współczynnik skalowania jest obliczany na source podstawie prostokąta, a następnie zastosowany do prostokąta source w wywołaniu metody :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);
        }
    }
    ···
}

Pierwsza z tych dwóch nowych DrawBitmap metod jest pokazana na stronie Tryby skalowania. Plik XAML zawiera trzy Picker elementy, które pozwalają wybrać elementy członkowskie i BitmapStretch BitmapAlignment wyliczenia:

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

Plik związany z kodem po prostu unieważnia CanvasView element, gdy dowolny Picker element uległ zmianie. Procedura PaintSurface obsługi uzyskuje dostęp do trzech Picker widoków w celu wywołania DrawBitmap metody rozszerzenia:

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

Oto kilka kombinacji opcji:

Tryby skalowania

Strona Podzestaw prostokąta ma praktycznie ten sam plik XAML co tryby skalowania, ale plik za kodem definiuje prostokątny podzbiór mapy bitowej podanej SOURCE przez pole:

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

To źródło prostokąta izoluje głowę małpy, jak pokazano na poniższych zrzutach ekranu:

Podzbiór prostokąta