Sdílet prostřednictvím


Bitmap Basics in SkiaSharp

Načtěte rastrové obrázky z různých zdrojů a zobrazte je.

Podpora rastrových obrázků ve SkiaSharpu je poměrně rozsáhlá. Tento článek se zabývá pouze základy – jak načíst rastrové obrázky a jak je zobrazit:

Zobrazení dvou rastrových obrázků

Mnohem hlubší zkoumání rastrových obrázků najdete v části SkiaSharp Bitmaps.

Bitmapa SkiaSharp je objekt typu SKBitmap. Existuje mnoho způsobů, jak vytvořit rastrový obrázek, ale tento článek omezuje sám na metodu SKBitmap.Decode , která načte rastrový obrázek z objektu .NET Stream .

Stránka Základní rastrové obrázky v programu SkiaSharpFormsDemos ukazuje, jak načíst rastrové obrázky ze tří různých zdrojů:

  • Z internetu
  • Z prostředku vloženého do spustitelného souboru
  • Z knihovny fotek uživatele

Tři SKBitmap objekty pro tyto tři zdroje jsou definovány jako pole ve BasicBitmapsPage třídě:

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

Načtení rastrového obrázku z webu

K načtení rastrového obrázku na základě adresy URL můžete použít HttpClient třídu. Měli byste vytvořit instanci pouze jedné instance HttpClient a znovu ji použít, takže ji uložte jako pole:

HttpClient httpClient = new HttpClient();

Při použití HttpClient s aplikacemi pro iOS a Android budete chtít nastavit vlastnosti projektu, jak je popsáno v dokumentech tls (Transport Layer Security) 1.2.

Vzhledem k tomu, že je nejvhodnější použít await operátor s HttpClient, nelze kód spustit v konstruktoru BasicBitmapsPage . Místo toho je součástí přepsání OnAppearing . Adresa URL zde odkazuje na oblast na webu Xamarin s některými ukázkovými rastrovými obrázky. Balíček na webu umožňuje připojit specifikaci pro změnu velikosti rastrového obrázku na určitou šířku:

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

Operační systém Android vyvolá výjimku při použití vráceného Stream z GetStreamAsync metody SKBitmap.Decode , protože provádí zdlouhavou operaci v hlavním vlákně. Z tohoto důvodu se obsah rastrového souboru zkopíruje do objektu MemoryStream pomocí CopyToAsync.

Statická SKBitmap.Decode metoda zodpovídá za dekódování rastrových souborů. Funguje s rastrovými formáty JPEG, PNG a GIF a výsledky ukládá do interního formátu SkiaSharp. V tomto okamžiku SKCanvasView je potřeba zneplatnit, aby obslužná rutina PaintSurface mohla aktualizovat zobrazení.

Načtení bitmapového prostředku

Z hlediska kódu je nejjednodušším způsobem načítání rastrových obrázků zahrnout rastrový prostředek přímo ve vaší aplikaci. Program SkiaSharpFormsDemos obsahuje složku s názvem Media obsahující několik rastrových souborů, včetně jedné pojmenované monkey.png. U rastrových obrázků uložených jako prostředky programu je nutné použít dialogové okno Vlastnosti k poskytnutí souboru akce sestavení vloženého prostředku!

Každý vložený prostředek má ID zdroje, které se skládá z názvu projektu, složky a názvu souboru, všechny spojené tečkami: SkiaSharpFormsDemos.Media.monkey.png. Přístup k tomuto prostředku získáte zadáním ID prostředku jako argumentu GetManifestResourceStream pro metodu Assembly třídy:

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

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

Tento Stream objekt lze předat přímo metodě SKBitmap.Decode .

Načtení rastrového obrázku z knihovny fotografií

Je také možné, aby uživatel načetl fotku z knihovny obrázků zařízení. Toto zařízení není poskytováno Xamarin.Forms samostatně. Úloha vyžaduje službu závislostí, například službu popsanou v článku Výběr fotky z knihovny obrázků.

Soubor IPhotoLibrary.cs v projektu SkiaSharpFormsDemos a tři PhotoLibrary.cs soubory v projektech platformy byly upraveny z daného článku. Kromě toho byl soubor MainActivity.cs androidu upraven tak, jak je popsáno v článku, a projekt iOS má oprávnění pro přístup ke knihovně fotografií se dvěma řádky v dolní části souboru info.plist.

Konstruktor BasicBitmapsPage přidá TapGestureRecognizer do upozornění SKCanvasView na klepnutí. Po přijetí klepnutí Tapped získá obslužná rutina přístup ke službě závislostí pro výběr obrázku a volání PickPhotoAsync. Stream Pokud se vrátí objekt, předá SKBitmap.Decode se metodě:

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

Všimněte si, že obslužná rutina Tapped také volá InvalidateSurface metodu objektu SKCanvasView . Tím se vygeneruje nové volání obslužné rutiny PaintSurface .

Zobrazení rastrových obrázků

Obslužná rutina PaintSurface musí zobrazit tři rastrové obrázky. Obslužná rutina předpokládá, že telefon je v režimu na výšku a rozdělí plátno svisle na tři stejné části.

První rastrový obrázek se zobrazí nejjednodušší DrawBitmap metodou. Stačí zadat souřadnice X a Y, kde má být umístěn levý horní roh rastrového obrázku:

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

SKPaint Přestože je parametr definovaný, má výchozí hodnotu a můžete ho null ignorovat. Pixely rastrového obrázku se jednoduše přenesou na pixely povrchu zobrazení s mapováním 1:1. Aplikaci pro tento SKPaint argument uvidíte v další části o průhlednosti SkiaSharpu.

Program může získat rozměry pixelu rastrového obrázku Width s vlastnostmi a Height vlastnostmi. Tyto vlastnosti umožňují programu vypočítat souřadnice pro umístění rastrového obrázku do středu horní třetiny plátna:

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

Další dva rastrové obrázky se zobrazí s verzí DrawBitmap s parametrem SKRect :

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

Třetí verze DrawBitmap má dva SKRect argumenty pro zadání obdélníkové podmnožina rastrového obrázku k zobrazení, ale tato verze se v tomto článku nepoužívá.

Tady je kód pro zobrazení rastrového obrázku načteného z vloženého rastrového obrázku prostředku:

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

Rastrový obrázek je roztažen na rozměry obdélníku, což je důvod, proč je opice vodorovně roztažena na těchto snímcích obrazovky:

Trojitý snímek obrazovky se stránkou Základní rastrové obrázky

Třetí obrázek – který můžete vidět pouze v případě, že program spustíte a načtete fotku z vlastní knihovny obrázků – se také zobrazí v obdélníku, ale umístění a velikost obdélníku se upraví tak, aby zachoval poměr stran rastrového obrázku. Tento výpočet je trochu složitější, protože vyžaduje výpočet měřítka na základě velikosti rastrového obrázku a cílového obdélníku a na střed obdélníku v této oblasti:

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

Pokud se z knihovny obrázků ještě nenačetl žádný rastrový obrázek, zobrazí blok nějaký text, else který uživatele vyzve k klepnutí na obrazovku.

Rastrové obrázky můžete zobrazit s různými stupni průhlednosti a další článek o Průhlednosti SkiaSharp popisuje, jak na to.