Отображение растровых карт SkiaSharp

Download Sample Скачайте пример

Тема растровых карт SkiaSharp была представлена в статье "Основные сведения о растровых картах" в SkiaSharp. В этой статье показаны три способа загрузки растровых изображений и три способа отображения растровых изображений. В этой статье рассматриваются методы загрузки растровых изображений и более глубокое использование DrawBitmap методов SKCanvas.

Displaying Sample

Методы DrawBitmapLattice рассматриваются в статье Сегментированные отображения растровых карт SkiaSharp.DrawBitmapNinePatch

Примеры на этой странице приведены в приложении SkiaSharpFormsDemos. На домашней странице этого приложения выберите Bitmaps SkiaSharp, а затем перейдите в раздел "Отображение растровых карт".

Загрузка растрового изображения

Растровое изображение, используемое приложением SkiaSharp, обычно поставляется из одного из трех разных источников:

  • Из Интернета
  • Из ресурса, внедренного в исполняемый файл
  • Из библиотеки фотографий пользователя

Кроме того, приложение SkiaSharp может создать новую растровую карту, а затем рисовать на ней или задавать битовые биты алгоритмически. Эти методы рассматриваются в статьях о создании и рисовании растровых карт SkiaSharp и доступе к растровым пикселям SkiaSharp.

В следующих трех примерах кода загрузки растрового изображения предполагается, что класс содержит поле типа SKBitmap:

SKBitmap bitmap;

Как говорится в статье Bitmap Basics в SkiaSharp, лучший способ загрузить растровое изображение через Интернет с классомHttpClient. Один экземпляр класса можно определить как поле:

HttpClient httpClient = new HttpClient();

При использовании HttpClient с приложениями iOS и Android необходимо задать свойства проекта, как описано в документах по протоколу TLS 1.2.

Код, который часто использует HttpClientawait оператор, поэтому он должен находиться в методе 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
{
    ···
}

Обратите внимание, что Stream полученный объект GetStreamAsync копируется в объект MemoryStream. Android не разрешает Stream обработку от HttpClient основного потока, за исключением асинхронных методов.

Это SKBitmap.DecodeStream делает много работы: объект, переданный в него, ссылается на блок памяти, содержащий всю растровую карту в одном из общих форматов файлов растрового изображения, как правило, JPEG, PNG или GIF. Метод Decode должен определить формат, а затем декодировать растровый файл в собственный внутренний формат растрового изображения SkiaSharp.

После вызова SKBitmap.Decodeкода, вероятно, будет недействительным CanvasView , чтобы обработчик смог отобразить только что PaintSurface загруженное растровое изображение.

Вторым способом загрузки растрового изображения является включение растрового изображения в качестве внедренного ресурса в библиотеку .NET Standard, на которую ссылается отдельные проекты платформы. Идентификатор ресурса передается методу GetManifestResourceStream . Этот идентификатор ресурса состоит из имени сборки, имени папки и имени файла ресурса, разделенного точками:

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

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

Файлы растровых карт также можно хранить в виде ресурсов в отдельном проекте платформы для iOS, Android и универсальная платформа Windows (UWP). Однако для загрузки этих растровых изображений требуется код, расположенный в проекте платформы.

Третий подход к получению растрового изображения — из библиотеки рисунков пользователя. В следующем коде используется служба зависимостей, включенная в приложение SkiaSharpFormsDemos. Библиотека .NET Standard SkiaSharpFormsDemo включает IPhotoLibrary интерфейс, а каждый из проектов платформы PhotoLibrary содержит класс, реализующий этот интерфейс.

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

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

Как правило, такой код также недействителен CanvasView таким образом, чтобы PaintSurface обработчик может отображать новое растровое изображение.

Класс SKBitmap определяет несколько полезных свойств, включая Width и Height, которые показывают размеры растрового изображения, а также множество методов, включая методы для создания растровых изображений, копирования их и предоставления битов пикселей.

Отображение в измерениях пикселей

Класс SkiaSharp Canvas определяет четыре DrawBitmap метода. Эти методы позволяют отображать растровые изображения двумя основными способами:

  • При SKPoint указании значения (или отдельно x и y значений) отображается растровое изображение в его измерениях пикселей. Пиксели растрового изображения сопоставляются непосредственно с пикселями отображения видео.
  • Указание прямоугольника приводит к растянутой растровой диаграмме до размера и формы прямоугольника.

Вы отображаете растровое изображение в его измерениях пикселей с DrawBitmap параметром SKPoint или DrawBitmap отдельными x параметрами y :

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

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

Эти два метода функционально идентичны. Указанная точка указывает расположение левого верхнего угла растрового изображения относительно холста. Так как разрешение пикселей мобильных устройств настолько высоко, небольшие растровые изображения обычно отображаются довольно крошечными на этих устройствах.

Необязательный SKPaint параметр позволяет отображать растровое изображение с помощью прозрачности. Для этого создайте SKPaint объект и задайте Color для свойства любое SKColor значение с альфа-каналом меньше 1. Например:

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

0x80, переданный в качестве последнего аргумента, указывает на прозрачность 50 %. Вы также можете задать альфа-канал на одном из предварительно определенных цветов:

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

Однако сам цвет не имеет значения. Только альфа-канал проверяется при использовании SKPaint объекта в вызове DrawBitmap .

Объект SKPaint также играет роль при отображении растровых изображений с помощью режимов смешения или эффектов фильтра. Они показаны в статьях SkiaSharp compositing и смешивания режимов и фильтров изображений SkiaSharp.

Страница "Измерения пикселей" в примере программы SkiaSharpFormsDemos отображает ресурс растрового изображения размером 320 пикселей, широкий на 240 пикселей:

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

Обработчик PaintSurface центризует x растровое изображение путем вычисления и значений на основе измерений пиксельной поверхности отображения и y измерений пикселя растрового изображения:

Pixel Dimensions

Если приложение хочет отобразить растровое изображение в левом верхнем углу, оно просто передает координаты (0, 0).

Метод загрузки растровых карт ресурсов

Многие из приведенных примеров потребуются для загрузки ресурсов растрового изображения. Статический BitmapExtensions класс в решении SkiaSharpFormsDemos содержит метод, который поможет:

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

Обратите внимание на Type параметр. Это может быть объект, связанный Type с любым типом в сборке, в которой хранится ресурс растрового изображения.

Этот LoadBitmapResource метод будет использоваться во всех последующих примерах, для которых требуются ресурсы растрового изображения.

Растяжение для заполнения прямоугольника

Класс SKCanvas также определяет метод, который отрисовывает DrawBitmap растровое изображение в прямоугольник, а другой метод, который отрисовывает прямоугольное подмножество DrawBitmap растрового изображения прямоугольником:

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

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

В обоих случаях растровое изображение растянуто для заполнения прямоугольника с именем dest. Во втором методе source прямоугольник позволяет выбрать подмножество растрового изображения. Прямоугольник dest относительно выходного устройства; source прямоугольник относительно растрового изображения.

Страница "Заливка прямоугольника " демонстрирует первую из этих двух методов, отображая ту же растровую карту, используемую в предыдущем примере в прямоугольнике того же размера, что и холст:

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

Обратите внимание на использование нового BitmapExtensions.LoadBitmapResource метода для задания SKBitmap поля. Прямоугольник назначения получается из Rect свойства SKImageInfo, которое дефрибирует размер поверхности отображения:

Fill Rectangle

Это обычно не то, что вы хотите. Изображение искажается по-разному в горизонтальных и вертикальных направлениях. При отображении растрового изображения в чем-то, отличном от размера пикселя, обычно требуется сохранить исходное соотношение пропорций растрового изображения.

Растяжение при сохранении пропорций

Растяжение растрового изображения при сохранении пропорций — это процесс, также известный как равномерное масштабирование. Этот термин предполагает алгоритмический подход. Одно из возможных решений отображается на странице универсального масштабирования :

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

Обработчик PaintSurface вычисляет scale фактор, который является минимальным соотношением ширины отображения и высоты до ширины и высоты растрового изображения. Затем x можно вычислить значения y для выравнивания масштабируемого растрового изображения в пределах ширины и высоты дисплея. Прямоугольник назначения имеет верхний левый угол и y нижний правый угол x этих значений, а также масштабируемую ширину и высоту растрового изображения:

Uniform Scaling

Переверните телефон боковую сторону, чтобы увидеть растровое изображение растянуто на эту область:

Uniform Scaling landscape

Преимущество использования этого scale фактора становится очевидным, если вы хотите реализовать немного другой алгоритм. Предположим, вы хотите сохранить пропорции растрового изображения, а также заполнить прямоугольник назначения. Единственным способом этого является обрезка части изображения, но вы можете реализовать этот алгоритм, просто изменив Math.MinMath.Max его в приведенном выше коде. Ниже приведен результат:

Uniform Scaling alternative

Пропорции растрового изображения сохраняются, но области слева и справа от растрового изображения обрезаются.

Универсальная функция отображения растрового изображения

Среды программирования на основе XAML (например, UWP и Xamarin.Forms) имеют возможность расширить или уменьшить размер растровых изображений при сохранении их пропорций. Хотя SkiaSharp не включает эту функцию, ее можно реализовать самостоятельно. Класс BitmapExtensions , включенный в приложение SkiaSharpFormsDemos , показывает, как. Класс определяет два новых DrawBitmap метода, выполняющих вычисление пропорций. Эти новые методы являются методами SKCanvasрасширения.

Новые DrawBitmap методы включают параметр типа BitmapStretch, перечисление, определенное в файле BitmapExtensions.cs :

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

Элементы None, FillUniformи UniformToFill члены совпадают с элементами перечисления UWPStretch. Аналогичное Xamarin.FormsAspect перечисление определяет элементы Fill, AspectFitи AspectFill.

Страница универсального масштабирования , показанная выше, сосредоточена на растровом изображении в прямоугольнике, но могут потребоваться другие параметры, такие как размещение растрового изображения в левой или правой части прямоугольника или сверху или вниз. Это цель перечисления BitmapAlignment :

public enum BitmapAlignment
{
    Start,
    Center,
    End
}

Параметры выравнивания не влияют при использовании с BitmapStretch.Fill.

Первая DrawBitmap функция расширения содержит прямоугольник назначения, но не имеет исходного прямоугольника. Значения по умолчанию определяются таким образом, чтобы в том случае, если вы хотите, чтобы растровое изображение было центрировано, необходимо указать 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);
        }
    }
    ···
}

Основной целью этого метода является вычисление коэффициента масштабирования, который scale затем применяется к ширине и высоте растрового изображения при вызове CalculateDisplayRect метода. Это метод, который вычисляет прямоугольник для отображения растрового изображения на основе горизонтального и вертикального выравнивания:

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

Класс BitmapExtensions содержит дополнительный DrawBitmap метод с исходным прямоугольником для указания подмножества растрового изображения. Этот метод аналогичен первому, за исключением того, что коэффициент масштабирования вычисляется на source основе прямоугольника, а затем применяется к source прямоугольнику в вызове 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);
        }
    }
    ···
}

Первый из этих двух новых DrawBitmap методов показан на странице "Режимы масштабирования". XAML-файл содержит три Picker элемента, которые позволяют выбирать элементы BitmapStretch и BitmapAlignment перечисления:

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

Файл программной части просто делает недействительным момент CanvasView , когда любой Picker элемент изменился. Обработчик PaintSurface обращается к трем Picker представлениям для вызова 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);
    }
}

Ниже приведены некоторые сочетания параметров:

Scaling Modes

Страница подмножества прямоугольника имеет практически тот же XAML-файл, что и режимы масштабирования, но файл кода определяет прямоугольное подмножество растрового изображения, заданного SOURCE полем:

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

Этот прямоугольник изолирует голову обезьяны, как показано на следующих снимках экрана:

Rectangle Subset