Partilhar via


Noções básicas de bitmap no SkiaSharp

Carregue bitmaps de várias fontes e exiba-os.

O suporte de bitmaps no SkiaSharp é bastante extenso. Este artigo aborda apenas o básico — como carregar bitmaps e como exibi-los:

A exibição de dois bitmaps

Uma exploração muito mais profunda de bitmaps pode ser encontrada na seção SkiaSharp Bitmaps.

Um bitmap SkiaSharp é um objeto do tipo SKBitmap. Há muitas maneiras de criar um bitmap, mas este artigo se restringe ao SKBitmap.Decode método, que carrega o bitmap de um objeto .NET Stream .

A página Bitmaps básicos no programa SkiaSharpFormsDemos demonstra como carregar bitmaps de três fontes diferentes:

  • Da Internet
  • A partir de um recurso incorporado no executável
  • Da biblioteca de fotos do usuário

Três SKBitmap objetos para essas três fontes são definidos como campos na BasicBitmapsPage classe:

public class BasicBitmapsPage : ContentPage
{
    SKCanvasView canvasView;
    SKBitmap webBitmap;
    SKBitmap resourceBitmap;
    SKBitmap libraryBitmap;

    public BasicBitmapsPage()
    {
        Title = "Basic Bitmaps";

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

Carregando um bitmap da Web

Para carregar um bitmap com base em uma URL, você pode usar a HttpClient classe. Você deve instanciar apenas uma instância e HttpClient reutilizá-la, portanto, armazene-a como um campo:

HttpClient httpClient = new HttpClient();

Ao usar HttpClient com aplicativos iOS e Android, convém definir as propriedades do projeto conforme descrito nos documentos em Transport Layer Security (TLS) 1.2.

Como é mais conveniente usar o await operador com HttpClient, o código não pode ser executado no BasicBitmapsPage construtor. Em vez disso, faz parte da OnAppearing substituição. A URL aqui aponta para uma área no site do Xamarin com alguns bitmaps de exemplo. Um pacote no site permite acrescentar uma especificação para redimensionar o bitmap para uma largura específica:

protected override async void OnAppearing()
{
    base.OnAppearing();

    // Load web bitmap.
    string url = "https://developer.xamarin.com/demo/IMG_3256.JPG?width=480";

    try
    {
        using (Stream stream = await httpClient.GetStreamAsync(url))
        using (MemoryStream memStream = new MemoryStream())
        {
            await stream.CopyToAsync(memStream);
            memStream.Seek(0, SeekOrigin.Begin);

            webBitmap = SKBitmap.Decode(memStream);
            canvasView.InvalidateSurface();
        };
    }
    catch
    {
    }
}

O sistema operacional Android gera uma exceção ao usar o StreamSKBitmap.Decode retornado de GetStreamAsync no método porque ele está executando uma operação longa em um thread principal. Por esse motivo, o conteúdo do arquivo bitmap é copiado para um MemoryStream objeto usando CopyToAsynco .

O método estático SKBitmap.Decode é responsável por decodificar arquivos bitmap. Ele funciona com os formatos de bitmap JPEG, PNG e GIF e armazena os resultados em um formato SkiaSharp interno. Neste ponto, o precisa ser invalidado SKCanvasView para permitir que o PaintSurface manipulador atualize a exibição.

Carregando um recurso de bitmap

Em termos de código, a abordagem mais fácil para carregar bitmaps é incluir um recurso de bitmap diretamente em seu aplicativo. O programa SkiaSharpFormsDemos inclui uma pasta chamada Media contendo vários arquivos bitmap, incluindo um chamado monkey.png. Para bitmaps armazenados como recursos de programa, você deve usar a caixa de diálogo Propriedades para dar ao arquivo uma Ação de Criação de Recurso Incorporado!

Cada recurso incorporado tem uma ID de recurso que consiste no nome do projeto, na pasta e no nome do arquivo, todos conectados por pontos: SkiaSharpFormsDemos.Media.monkey.png. Você pode obter acesso a esse recurso especificando essa ID de recurso como um argumento para o GetManifestResourceStreamAssembly método da classe:

string resourceID = "SkiaSharpFormsDemos.Media.monkey.png";
Assembly assembly = GetType().GetTypeInfo().Assembly;

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

Esse Stream objeto pode ser passado diretamente para o SKBitmap.Decode método.

Carregando um bitmap da biblioteca de fotos

Também é possível que o usuário carregue uma foto da biblioteca de imagens do dispositivo. Esta facilidade não é fornecida por Xamarin.Forms si só. O trabalho requer um serviço de dependência, como o descrito no artigo Escolhendo uma foto da Biblioteca de Imagens.

O arquivo IPhotoLibrary.cs no projeto SkiaSharpFormsDemos e os três arquivos PhotoLibrary.cs nos projetos da plataforma foram adaptados desse artigo. Além disso, o arquivo MainActivity.cs do Android foi modificado conforme descrito no artigo, e o projeto iOS recebeu permissão para acessar a biblioteca de fotos com duas linhas na parte inferior do arquivo info.plist.

O BasicBitmapsPage construtor adiciona um TapGestureRecognizer ao SKCanvasView a ser notificado de torneiras. Ao receber um toque, o Tapped manipulador obtém acesso ao serviço de dependência do seletor de imagens e às chamadas PickPhotoAsync. Se um Stream objeto for retornado, ele será passado para o SKBitmap.Decode método:

// Add tap gesture recognizer
TapGestureRecognizer tapRecognizer = new TapGestureRecognizer();
tapRecognizer.Tapped += async (sender, args) =>
{
    // Load bitmap from photo library
    IPhotoLibrary photoLibrary = DependencyService.Get<IPhotoLibrary>();

    using (Stream stream = await photoLibrary.PickPhotoAsync())
    {
        if (stream != null)
        {
            libraryBitmap = SKBitmap.Decode(stream);
            canvasView.InvalidateSurface();
        }
    }
};
canvasView.GestureRecognizers.Add(tapRecognizer);

Observe que o Tapped manipulador também chama o InvalidateSurface método do SKCanvasView objeto. Isso gera uma nova chamada para o PaintSurface manipulador.

Exibindo os bitmaps

O PaintSurface manipulador precisa exibir três bitmaps. O manipulador assume que o telefone está no modo retrato e divide a tela verticalmente em três partes iguais.

O primeiro bitmap é exibido com o método mais DrawBitmap simples. Tudo o que você precisa especificar são as coordenadas X e Y onde o canto superior esquerdo do bitmap deve ser posicionado:

public void DrawBitmap (SKBitmap bitmap, Single x, Single y, SKPaint paint = null)

Embora um SKPaint parâmetro esteja definido, ele tem um valor padrão de null e você pode ignorá-lo. Os pixels do bitmap são simplesmente transferidos para os pixels da superfície de exibição com um mapeamento um-para-um. Você verá um aplicativo para esse SKPaint argumento na próxima seção sobre Transparência SkiaSharp.

Um programa pode obter as dimensões de pixel de um bitmap com as Width propriedades e Height . Essas propriedades permitem que o programa calcule coordenadas para posicionar o bitmap no centro do terço superior da tela:

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

    canvas.Clear();

    if (webBitmap != null)
    {
        float x = (info.Width - webBitmap.Width) / 2;
        float y = (info.Height / 3 - webBitmap.Height) / 2;
        canvas.DrawBitmap(webBitmap, x, y);
    }
    ...
}

Os outros dois bitmaps são exibidos com uma versão de DrawBitmap com um SKRect parâmetro:

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

Uma terceira versão do DrawBitmap tem dois SKRect argumentos para especificar um subconjunto retangular do bitmap a ser exibido, mas essa versão não é usada neste artigo.

Este é o código para exibir o bitmap carregado de um bitmap de recurso incorporado:

void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
    ...
    if (resourceBitmap != null)
    {
        canvas.DrawBitmap(resourceBitmap,
            new SKRect(0, info.Height / 3, info.Width, 2 * info.Height / 3));
    }
    ...
}

O bitmap é esticado até as dimensões do retângulo, e é por isso que o macaco é esticado horizontalmente nestas capturas de tela:

Uma captura de tela tripla da página Bitmaps Básicos

A terceira imagem — que você só pode ver se executar o programa e carregar uma foto de sua própria biblioteca de imagens — também é exibida dentro de um retângulo, mas a posição e o tamanho do retângulo são ajustados para manter a proporção do bitmap. Esse cálculo é um pouco mais envolvido porque requer o cálculo de um fator de dimensionamento com base no tamanho do bitmap e do retângulo de destino e centralização do retângulo nessa área:

void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
    ...
    if (libraryBitmap != null)
    {
        float scale = Math.Min((float)info.Width / libraryBitmap.Width,
                               info.Height / 3f / libraryBitmap.Height);

        float left = (info.Width - scale * libraryBitmap.Width) / 2;
        float top = (info.Height / 3 - scale * libraryBitmap.Height) / 2;
        float right = left + scale * libraryBitmap.Width;
        float bottom = top + scale * libraryBitmap.Height;
        SKRect rect = new SKRect(left, top, right, bottom);
        rect.Offset(0, 2 * info.Height / 3);

        canvas.DrawBitmap(libraryBitmap, rect);
    }
    else
    {
        using (SKPaint paint = new SKPaint())
        {
            paint.Color = SKColors.Blue;
            paint.TextAlign = SKTextAlign.Center;
            paint.TextSize = 48;

            canvas.DrawText("Tap to load bitmap",
                info.Width / 2, 5 * info.Height / 6, paint);
        }
    }
}

Se nenhum bitmap ainda tiver sido carregado da biblioteca de imagens, o bloco exibirá else algum texto para solicitar que o usuário toque na tela.

Você pode exibir bitmaps com vários graus de transparência, e o próximo artigo sobre SkiaSharp Transparency descreve como.