Vytváření a kreslení na rastrové obrázky SkiaSharp

Viděli jste, jak může aplikace načítat rastrové obrázky z webu, z prostředků aplikace a z knihovny fotek uživatele. V aplikaci je také možné vytvořit nové rastrové obrázky. Nejjednodušší přístup zahrnuje jeden z konstruktorů SKBitmap:

SKBitmap bitmap = new SKBitmap(width, height);

height Parametry width jsou celá čísla a určují rozměry pixelu rastrového obrázku. Tento konstruktor vytvoří fullbarevný rastrový obrázek se čtyřmi bajty na pixel: jeden bajt každý pro červené, zelené, modré a alfa (neprůhledné) komponenty.

Po vytvoření nového rastrového obrázku potřebujete něco získat na povrchu rastrového obrázku. Obvykle to uděláte jedním ze dvou způsobů:

  • Kreslete na rastrový obrázek pomocí standardních Canvas metod kreslení.
  • Přístup k bitům pixelů přímo

Tento článek ukazuje první přístup:

Ukázka výkresu

Druhý přístup je popsán v článku Přístup k bitmapám SkiaSharp Pixels.

Kreslení na rastrový obrázek

Kreslení na povrchu rastrového obrázku je stejné jako kreslení na zobrazení videa. Pokud chcete kreslit na zobrazení videa, získáte SKCanvas objekt z PaintSurface argumentů události. Pokud chcete nakreslit rastrový obrázek, vytvoříte SKCanvas objekt pomocí konstruktoru SKCanvas :

SKCanvas canvas = new SKCanvas(bitmap);

Po dokončení kreslení na rastrový obrázek můžete objekt SKCanvas odstranit. Z tohoto důvodu SKCanvas je konstruktor obecně volána v using příkazu:

using (SKCanvas canvas = new SKCanvas(bitmap))
{
    ··· // call drawing function
}

Rastrový obrázek se pak dá zobrazit. Později může program vytvořit nový SKCanvas objekt na základě stejného rastrového obrázku a nakreslit na něj ještě něco dalšího.

Stránka Hello Bitmap v ukázkové aplikaci zapíše text "Hello, Bitmap!" na rastrový obrázek a pak tento rastrový obrázek několikrát zobrazí.

Konstruktor začíná HelloBitmapPage vytvořením objektu SKPaint pro zobrazení textu. Určuje rozměry textového řetězce a vytvoří rastrový obrázek s těmito rozměry. Pak vytvoří SKCanvas objekt založený na této bitmapě, volání Cleara potom volání DrawText. Vždy je vhodné volat Clear s novým rastrovým obrázkem, protože nově vytvořený rastrový obrázek může obsahovat náhodná data.

Konstruktor končí vytvořením objektu SKCanvasView pro zobrazení rastrového obrázku:

public partial class HelloBitmapPage : ContentPage
{
    const string TEXT = "Hello, Bitmap!";
    SKBitmap helloBitmap;

    public HelloBitmapPage()
    {
        Title = TEXT;

        // Create bitmap and draw on it
        using (SKPaint textPaint = new SKPaint { TextSize = 48 })
        {
            SKRect bounds = new SKRect();
            textPaint.MeasureText(TEXT, ref bounds);

            helloBitmap = new SKBitmap((int)bounds.Right,
                                       (int)bounds.Height);

            using (SKCanvas bitmapCanvas = new SKCanvas(helloBitmap))
            {
                bitmapCanvas.Clear();
                bitmapCanvas.DrawText(TEXT, 0, -bounds.Top, textPaint);
            }
        }

        // Create SKCanvasView to view result
        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(SKColors.Aqua);

        for (float y = 0; y < info.Height; y += helloBitmap.Height)
            for (float x = 0; x < info.Width; x += helloBitmap.Width)
            {
                canvas.DrawBitmap(helloBitmap, x, y);
            }
    }
}

Obslužná rutina PaintSurface vykreslí rastrový obrázek několikrát v řádcích a sloupcích zobrazení. Všimněte si, že Clear metoda v PaintSurface obslužné rutině má argument , který SKColors.Aquabarvy pozadí plochy zobrazení:

Dobrý den, Bitmape!

Vzhled aqua background odhalí, že rastrový obrázek je průhledný s výjimkou textu.

Vymazání a transparentnost

Zobrazení stránky Hello Bitmap ukazuje, že rastrový obrázek vytvořený program je průhledný s výjimkou černého textu. To je důvod, proč aqua barva povrchu displeje ukazuje skrz.

Dokumentace metodSKCanvas, které Clear je popisují pomocí příkazu: "Nahradí všechny pixely v aktuálním klipu plátna". Použití slova "replaces" odhalí důležitou charakteristiku těchto metod: Všechny metody SKCanvas kreslení přidají něco na existující plochu zobrazení. Metody Clearnahrazují , co tam už je.

Clear existuje ve dvou různých verzích:

  • Metoda Clear s SKColor parametrem nahradí pixely povrchu displeje pixely této barvy.

  • Metoda Clear bez parametrů nahrazuje pixely SKColors.Empty barvou, což je barva, ve které jsou všechny komponenty (červené, zelené, modré a alfa) nastaveny na nulu. Tato barva se někdy označuje jako průhledná černá.

Volání Clear bez argumentů na novém rastrovém obrázku inicializuje celý rastr, aby byl zcela transparentní. Cokoli následně nakreslené na rastrovém obrázku bude obvykle neprůhlásné nebo částečně neprůhlásné.

Tady je něco, co se má vyzkoušet: Na stránce Hello Bitmap nahraďte metodu Clear použitou bitmapCanvas na tuto:

bitmapCanvas.Clear(new SKColor(255, 0, 0, 128));

Pořadí parametrů konstruktoru SKColor je červené, zelené, modré a alfa, kde každá hodnota může být v rozsahu od 0 do 255. Mějte na paměti, že alfa hodnota 0 je průhledná, zatímco alfa hodnota 255 je neprůhledná.

Hodnota (255, 0, 0, 128) vymaže rastrové pixely na červené pixely s 50% neprůhledností. To znamená, že pozadí rastrového obrázku je poloprůhledné. Poloprůhledné červené pozadí rastrového obrázku kombinuje s aqua pozadím povrchu displeje a vytvoří šedé pozadí.

Zkuste nastavit barvu textu na průhlednou černou tak, že do inicializátoru SKPaint vložíte následující přiřazení:

Color = new SKColor(0, 0, 0, 0)

Možná si myslíte, že tento průhledný text by vytvořil plně průhledné oblasti rastrového obrázku, kterými byste viděli aqua pozadí povrchu zobrazení. Ale to není tak. Text se nakreslený nad obsahem rastrového obrázku. Průhledný text nebude vůbec viditelný.

Žádná Draw metoda nikdy nezprůhlední rastrový obrázek. Jen Clear to může udělat.

Typy barev rastrových obrázků

Nejjednodušší SKBitmap konstruktor umožňuje zadat celočíselnou šířku a výšku pixelu rastrového obrázku. Další SKBitmap konstruktory jsou složitější. Tyto konstruktory vyžadují argumenty dvou typů výčtu: SKColorType a SKAlphaType. Ostatní konstruktory používají SKImageInfo strukturu, která tyto informace konsoliduje.

Výčet SKColorType má 9 členů. Každý z těchto členů popisuje konkrétní způsob ukládání rastrových pixelů:

  • Unknown
  • Alpha8 — každý pixel je 8 bitů, což představuje alfa hodnotu z plně průhledné až po zcela neprůhlednou.
  • Rgb565 — každý pixel je 16 bitů, 5 bitů pro červenou a modrou a 6 pro zelenou
  • Argb4444 — každý pixel je 16 bitů, 4 pro alfa, červenou, zelenou a modrou
  • Rgba8888 — každý pixel je 32 bitů, 8 pro červenou, zelenou, modrou a alfa
  • Bgra8888 — každý pixel je 32 bitů, 8 z nich pro modrou, zelenou, červenou a alfa
  • Index8 — každý pixel je 8 bitů a představuje index do SKColorTable
  • Gray8 — každý pixel je 8 bitů představující šedý odstín z černé na bílou.
  • RgbaF16 — každý pixel je 64 bitů s červeným, zeleným, modrým a alfa ve 16bitovém formátu s plovoucí desetinou čárkou.

Dva formáty, ve kterých je každý pixel 32 pixelů (4 bajty), se často označují jako celobarevné formáty. Mnoho dalších formátů datum od času, kdy se video zobrazuje jako první, nebylo schopno mít plnou barvu. Rastrové obrázky s omezenou barvou byly adekvátní pro tyto displeje a umožňovaly rastrové obrázky zabírat méně místa v paměti.

V těchto dnech programátoři téměř vždy používají celobarevné rastrové obrázky a nemají obtěžovat s jinými formáty. Výjimkou je RgbaF16 formát, který umožňuje větší rozlišení barev než dokonce celobarevné formáty. Tento formát se ale používá pro specializované účely, jako je lékařské zobrazování, a při použití se standardními fullbarevnými displeji moc nedává smysl.

Tato série článků se omezí na SKBitmap barevné formáty používané ve výchozím nastavení, pokud není zadán žádný SKColorType člen. Tento výchozí formát je založený na podkladové platformě. Pro platformy podporované výchozím typem Xamarin.Formsbarvy je:

  • Rgba8888 pro iOS a Android
  • Bgra8888 pro UPW

Jediným rozdílem je pořadí 4 bajtů v paměti a stává se to jen problémem, když přímo přistupujete k bitům pixelů. Nebude to důležité, dokud se nedostanete na článek Přístup k bitmapovým pixelům SkiaSharp.

Výčet SKAlphaType má čtyři členy:

  • Unknown
  • Opaque — rastrový obrázek nemá žádnou průhlednost.
  • Premul — barevné součásti jsou předem vynásobeny alfa komponentou.
  • Unpremul — barevné součásti nejsou předem vynásobeny alfa komponentou

Tady je 4bajtů červený rastrový obrázek s 50% průhledností s bajty zobrazenými v pořadí červené, zelené, modré, alfa:

0xFF 0x00 0x00 0x80

Pokud je rastrový obrázek obsahující poloprůhledné pixely vykreslen na ploše zobrazení, musí být barevné součásti každého rastrového pixelu vynásobeny alfa hodnotou daného pixelu a barevné součásti odpovídajícího pixelu povrchu zobrazení musí být vynásobeny 255 minus alfa hodnota. Tyto dva pixely je pak možné zkombinovat. Rastrový obrázek lze vykreslit rychleji, pokud barevné komponenty v pixelech rastru již byly předem ztlumené hodnotou alfa. Stejný červený pixel by byl uložený jako tento v přednásobeném formátu:

0x80 0x00 0x00 0x80

Toto zlepšení výkonu je důvodem, proč SkiaSharp jsou rastrové obrázky ve výchozím nastavení vytvořeny ve Premul formátu. Ale znovu, to je nutné vědět pouze v případě, že přistupujete k pixelům a manipulujete s nimi.

Kreslení na existující rastrové obrázky

Není nutné vytvořit nový rastrový obrázek, který na něj bude kreslit. Můžete také kreslit na existující bitmapu.

Stránka Monkey Moustache používá svůj konstruktor k načtení MonkeyFace.png obrázku. Potom vytvoří SKCanvas objekt založený na této bitmapě a použije SKPaint a SKPath objekty k nakreslení knírek na něj:

public partial class MonkeyMoustachePage : ContentPage
{
    SKBitmap monkeyBitmap;

    public MonkeyMoustachePage()
    {
        Title = "Monkey Moustache";

        monkeyBitmap = BitmapExtensions.LoadBitmapResource(GetType(),
            "SkiaSharpFormsDemos.Media.MonkeyFace.png");

        // Create canvas based on bitmap
        using (SKCanvas canvas = new SKCanvas(monkeyBitmap))
        {
            using (SKPaint paint = new SKPaint())
            {
                paint.Style = SKPaintStyle.Stroke;
                paint.Color = SKColors.Black;
                paint.StrokeWidth = 24;
                paint.StrokeCap = SKStrokeCap.Round;

                using (SKPath path = new SKPath())
                {
                    path.MoveTo(380, 390);
                    path.CubicTo(560, 390, 560, 280, 500, 280);

                    path.MoveTo(320, 390);
                    path.CubicTo(140, 390, 140, 280, 200, 280);

                    canvas.DrawPath(path, paint);
                }
            }
        }

        // Create SKCanvasView to view result
        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(monkeyBitmap, info.Rect, BitmapStretch.Uniform);
    }
}

Konstruktor končí vytvořením SKCanvasView obslužné rutiny, jejíž PaintSurface obslužná rutina jednoduše zobrazí výsledek:

Opice Moustache

Kopírování a úpravy rastrových obrázků

Metody SKCanvas , které lze použít k kreslení na rastrový obrázek zahrnout DrawBitmap. To znamená, že můžete nakreslit jeden rastrový obrázek na jiný, obvykle ho nějakým způsobem upravit.

Nejobjemnější způsob, jak upravit rastrový obrázek, je přístup k skutečným bitům pixelů, předmět popsaný v článku Přístup k rastrovým pixelům SkiaSharp. Existuje ale mnoho dalších technik pro úpravu rastrových obrázků, které nevyžadují přístup k bitům pixelů.

Následující rastrový obrázek, který je součástí ukázkové aplikace, je široký 360 pixelů a výška 480 pixelů:

Horolezci

Předpokládejme, že jste nedostali oprávnění od opice na levé straně k publikování této fotografie. Jedním zřešeních Pixely tváře jsou nahrazeny bloky barev, takže není možné tyto funkce vytvůrovat. Bloky barev se obvykle odvozují z původního obrázku průměrem barev pixelů odpovídajících těmto blokům. Ale nemusíte to provádět průměrně. Stává se to automaticky, když zkopírujete rastrový obrázek do menšího rozměru pixelů.

Obličej levé opice zabírá přibližně 72 pixelů čtvercovou plochu s levým horním rohem v bodě (112, 238). Pojďme tuto 72 pixelovou čtvercovou oblast nahradit 9-bajtů-9 polem barevných bloků, z nichž každý je 8×8 pixelů čtvercový.

Stránka Pixelize Obrázek se načte v této bitmapě a nejprve vytvoří malý 9 pixelů čtvercový rastr s názvem faceBitmap. To je cíl pro kopírování jen opice obličeje. Cílový obdélník je jen 9 pixelů čtverce, ale zdrojový obdélník je čtvercový 72 pixelů. Každý 8 až 8 bloků zdrojových pixelů je konsolidovaný až na jeden pixel průměrem barev.

Dalším krokem je zkopírování původního rastrového obrázku do nového rastrového obrázku se stejnou velikostí, která se nazývá pixelizedBitmap. Malá faceBitmap je poté zkopírována na to s obdélníkem 72 pixelů čtvercového obdélníku tak, aby každý pixel faceBitmap byl rozšířen na 8krát jeho velikost:

public class PixelizedImagePage : ContentPage
{
    SKBitmap pixelizedBitmap;

    public PixelizedImagePage ()
    {
        Title = "Pixelize Image";

        SKBitmap originalBitmap = BitmapExtensions.LoadBitmapResource(GetType(),
            "SkiaSharpFormsDemos.Media.MountainClimbers.jpg");

        // Create tiny bitmap for pixelized face
        SKBitmap faceBitmap = new SKBitmap(9, 9);

        // Copy subset of original bitmap to that
        using (SKCanvas canvas = new SKCanvas(faceBitmap))
        {
            canvas.Clear();
            canvas.DrawBitmap(originalBitmap,
                              new SKRect(112, 238, 184, 310),   // source
                              new SKRect(0, 0, 9, 9));          // destination

        }

        // Create full-sized bitmap for copy
        pixelizedBitmap = new SKBitmap(originalBitmap.Width, originalBitmap.Height);

        using (SKCanvas canvas = new SKCanvas(pixelizedBitmap))
        {
            canvas.Clear();

            // Draw original in full size
            canvas.DrawBitmap(originalBitmap, new SKPoint());

            // Draw tiny bitmap to cover face
            canvas.DrawBitmap(faceBitmap,
                              new SKRect(112, 238, 184, 310));  // destination
        }

        // Create SKCanvasView to view result
        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(pixelizedBitmap, info.Rect, BitmapStretch.Uniform);
    }
}

Konstruktor dospěl k závěru vytvořením výsledku SKCanvasView , který zobrazí výsledek:

Pixelizace obrázku

Obměny rastrových obrázků

Dalším běžným úkolem je rotace rastrových obrázků. To je zvlášť užitečné při načítání rastrových obrázků z knihovny fotek i Telefon nebo iPad. Pokud se zařízení při pořízené fotce nedrželo v určité orientaci, bude obrázek pravděpodobně vzhůru nohama nebo do strany.

Otočení rastrového obrázku vzhůru nohama vyžaduje vytvoření dalšího rastrového obrázku se stejnou velikostí jako první a nastavení transformace, která se má otočit o 180 stupňů při kopírování první na druhou. Ve všech příkladech v této části je SKBitmap objekt, bitmap který potřebujete otočit:

SKBitmap rotatedBitmap = new SKBitmap(bitmap.Width, bitmap.Height);

using (SKCanvas canvas = new SKCanvas(rotatedBitmap))
{
    canvas.Clear();
    canvas.RotateDegrees(180, bitmap.Width / 2, bitmap.Height / 2);
    canvas.DrawBitmap(bitmap, new SKPoint());
}

Při otáčení o 90 stupňů musíte vytvořit rastrový obrázek, který je jiný než původní, a to tak, že prohodí výšku a šířku. Pokud je například původní rastrový obrázek široký 1200 pixelů a vysoký 800 pixelů, otočený rastrový obrázek je široký 800 pixelů a široký 1200 pixelů. Nastavte překlad a otočení tak, aby se rastrový obrázek otočil kolem levého horního rohu a pak se přesunul do zobrazení. (Mějte na paměti, že Translate metody a RotateDegrees metody jsou volána v opačném pořadí způsobu, jakým jsou použity.) Tady je kód pro otáčení o 90 stupňů ve směru hodinových ručiček:

SKBitmap rotatedBitmap = new SKBitmap(bitmap.Height, bitmap.Width);

using (SKCanvas canvas = new SKCanvas(rotatedBitmap))
{
    canvas.Clear();
    canvas.Translate(bitmap.Height, 0);
    canvas.RotateDegrees(90);
    canvas.DrawBitmap(bitmap, new SKPoint());
}

A tady je podobná funkce pro otáčení 90 stupňů proti směru hodinových ručiček:

SKBitmap rotatedBitmap = new SKBitmap(bitmap.Height, bitmap.Width);

using (SKCanvas canvas = new SKCanvas(rotatedBitmap))
{
    canvas.Clear();
    canvas.Translate(0, bitmap.Width);
    canvas.RotateDegrees(-90);
    canvas.DrawBitmap(bitmap, new SKPoint());
}

Tyto dvě metody se používají na stránkách Foto Puzzle popsané v článku Oříznutí SkiaSharp Bitmaps.

Program, který uživateli umožňuje otočit rastrový obrázek v 90stupňových přírůstcích, potřebuje implementovat pouze jednu funkci pro otáčení o 90 stupňů. Uživatel pak může otáčet v libovolném přírůstku o 90 stupňů opakovaným spuštěním této funkce.

Program může rastrový obrázek také otočit o libovolnou velikost. Jedním z jednoduchých přístupů je úprava funkce, která otočí o 180 stupňů nahrazením 180 generalizovanou angle proměnnou:

SKBitmap rotatedBitmap = new SKBitmap(bitmap.Width, bitmap.Height);

using (SKCanvas canvas = new SKCanvas(rotatedBitmap))
{
    canvas.Clear();
    canvas.RotateDegrees(angle, bitmap.Width / 2, bitmap.Height / 2);
    canvas.DrawBitmap(bitmap, new SKPoint());
}

V obecném případě však tato logika ořízne rohy otočené bitmapy. Lepším přístupem je vypočítat velikost otočného rastrového obrázku pomocí trigonometrie k zahrnutí těchto rohů.

Tato trigonometrické funkce je zobrazena na stránce Bitmap Rotator . Soubor XAML vytvoří SKCanvasView instanci a hodnotu Slider , která může být v rozsahu od 0 do 360 stupňů a Label zobrazuje aktuální hodnotu:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:skia="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
             x:Class="SkiaSharpFormsDemos.Bitmaps.BitmapRotatorPage"
             Title="Bitmap Rotator">
    <StackLayout>
        <skia:SKCanvasView x:Name="canvasView"
                           VerticalOptions="FillAndExpand"
                           PaintSurface="OnCanvasViewPaintSurface" />

        <Slider x:Name="slider"
                Maximum="360"
                Margin="10, 0"
                ValueChanged="OnSliderValueChanged" />

        <Label Text="{Binding Source={x:Reference slider},
                              Path=Value,
                              StringFormat='Rotate by {0:F0}&#x00B0;'}"
               HorizontalTextAlignment="Center" />

    </StackLayout>
</ContentPage>

Soubor s kódem načte rastrový zdroj a uloží ho jako statické pole jen pro čtení s názvem originalBitmap. Rastrový obrázek zobrazený v obslužné rutině PaintSurface je rotatedBitmap, který je původně nastaven na originalBitmap:

public partial class BitmapRotatorPage : ContentPage
{
    static readonly SKBitmap originalBitmap =
        BitmapExtensions.LoadBitmapResource(typeof(BitmapRotatorPage),
            "SkiaSharpFormsDemos.Media.Banana.jpg");

    SKBitmap rotatedBitmap = originalBitmap;

    public BitmapRotatorPage ()
    {
        InitializeComponent ();
    }

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

        canvas.Clear();
        canvas.DrawBitmap(rotatedBitmap, info.Rect, BitmapStretch.Uniform);
    }

    void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
    {
        double angle = args.NewValue;
        double radians = Math.PI * angle / 180;
        float sine = (float)Math.Abs(Math.Sin(radians));
        float cosine = (float)Math.Abs(Math.Cos(radians));
        int originalWidth = originalBitmap.Width;
        int originalHeight = originalBitmap.Height;
        int rotatedWidth = (int)(cosine * originalWidth + sine * originalHeight);
        int rotatedHeight = (int)(cosine * originalHeight + sine * originalWidth);

        rotatedBitmap = new SKBitmap(rotatedWidth, rotatedHeight);

        using (SKCanvas canvas = new SKCanvas(rotatedBitmap))
        {
            canvas.Clear(SKColors.LightPink);
            canvas.Translate(rotatedWidth / 2, rotatedHeight / 2);
            canvas.RotateDegrees((float)angle);
            canvas.Translate(-originalWidth / 2, -originalHeight / 2);
            canvas.DrawBitmap(originalBitmap, new SKPoint());
        }

        canvasView.InvalidateSurface();
    }
}

Obslužná ValueChanged rutina Slider provádí operace, které vytvoří novou rotatedBitmap na základě úhlu otáčení. Nová šířka a výška jsou založeny na absolutních hodnotách sines a kosinus původních šířek a výšek. Transformace použité k vykreslení původního rastrového obrázku na otočeném rastrovém obrázku přesunou původní střed rastrového obrázku do počátku, pak ho otočí o zadaný počet stupňů a pak přeloží toto střed na střed otočené bitmapy. (Metody Translate a RotateDegrees metody se volají v opačném pořadí, než jak se používají.)

Všimněte si použití Clear metody k vytvoření pozadí rotatedBitmap světle růžové. To je pouze pro ilustraci velikosti rotatedBitmap na displeji:

Rastrový rotátor

Otočený rastrový obrázek je dostatečně velký, aby zahrnoval celý původní rastrový obrázek, ale není větší.

Překlopení rastrových obrázků

Další operací, která se běžně provádí u rastrových obrázků, se nazývá překlopení. Rastrový obrázek se koncepčně otočí ve třech rozměrech kolem svislé osy nebo vodorovné osy přes střed rastrového obrázku. Svislé překlopení vytvoří zrcadlový obrázek.

Stránka Bitmap Flipper v ukázkové aplikaci demonstruje tyto procesy. Soubor XAML obsahuje SKCanvasView dvě tlačítka pro překlopení svisle a vodorovně:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:skia="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
             x:Class="SkiaSharpFormsDemos.Bitmaps.BitmapFlipperPage"
             Title="Bitmap Flipper">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

        <skia:SKCanvasView x:Name="canvasView"
                           Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"
                           PaintSurface="OnCanvasViewPaintSurface" />

        <Button Text="Flip Vertical"
                Grid.Row="1" Grid.Column="0"
                Margin="0, 10"
                Clicked="OnFlipVerticalClicked" />

        <Button Text="Flip Horizontal"
                Grid.Row="1" Grid.Column="1"
                Margin="0, 10"
                Clicked="OnFlipHorizontalClicked" />
    </Grid>
</ContentPage>

Soubor s kódem implementuje tyto dvě operace v Clicked obslužných rutinách tlačítek:

public partial class BitmapFlipperPage : ContentPage
{
    SKBitmap bitmap =
        BitmapExtensions.LoadBitmapResource(typeof(BitmapRotatorPage),
            "SkiaSharpFormsDemos.Media.SeatedMonkey.jpg");

    public BitmapFlipperPage()
    {
        InitializeComponent();
    }

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

    void OnFlipVerticalClicked(object sender, ValueChangedEventArgs args)
    {
        SKBitmap flippedBitmap = new SKBitmap(bitmap.Width, bitmap.Height);

        using (SKCanvas canvas = new SKCanvas(flippedBitmap))
        {
            canvas.Clear();
            canvas.Scale(-1, 1, bitmap.Width / 2, 0);
            canvas.DrawBitmap(bitmap, new SKPoint());
        }

        bitmap = flippedBitmap;
        canvasView.InvalidateSurface();
    }

    void OnFlipHorizontalClicked(object sender, ValueChangedEventArgs args)
    {
        SKBitmap flippedBitmap = new SKBitmap(bitmap.Width, bitmap.Height);

        using (SKCanvas canvas = new SKCanvas(flippedBitmap))
        {
            canvas.Clear();
            canvas.Scale(1, -1, 0, bitmap.Height / 2);
            canvas.DrawBitmap(bitmap, new SKPoint());
        }

        bitmap = flippedBitmap;
        canvasView.InvalidateSurface();
    }
}

Vertikální překlopení se provádí transformací škálování s horizontálním faktorem škálování –1. Měřítko je svislým středem rastrového obrázku. Horizontální překlopení je transformace škálování se svislým měřítkem –1.

Jak vidíte z obráceného dopisování na opičí košili, překlopení není stejné jako otočení. Snímek obrazovky UPW na pravé straně ukazuje, že překlopení vodorovně i svisle je stejné jako otočení o 180 stupňů:

Rastrový překlopení

Dalším běžným úkolem, který lze zpracovat pomocí podobných technik, je oříznutí rastrového obrázku na obdélníkovou podmnožinu. Toto je popsáno v dalším článku Oříznutí rastrových obrázků SkiaSharp.