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:
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í Clear
a 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.Aqua
barvy pozadí plochy zobrazení:
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 Clear
nahrazují , co tam už je.
Clear
existuje ve dvou různých verzích:
Metoda
Clear
sSKColor
parametrem nahradí pixely povrchu displeje pixely této barvy.Metoda
Clear
bez parametrů nahrazuje pixelySKColors.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 zelenouArgb4444
— každý pixel je 16 bitů, 4 pro alfa, červenou, zelenou a modrouRgba8888
— každý pixel je 32 bitů, 8 pro červenou, zelenou, modrou a alfaBgra8888
— každý pixel je 32 bitů, 8 z nich pro modrou, zelenou, červenou a alfaIndex8
— každý pixel je 8 bitů a představuje index doSKColorTable
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 AndroidBgra8888
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:
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ů:
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:
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}°'}"
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:
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ňů:
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.