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:
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 Stream
SKBitmap.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 CopyToAsync
o .
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 GetManifestResourceStream
Assembly
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:
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.