Tiling mapy bitowej SkiaSharp
Jak pokazano w dwóch poprzednich artykułach, SKShader
klasa może tworzyć gradienty liniowe lub cykliczne. Ten artykuł koncentruje się na SKShader
obiekcie, który używa mapy bitowej do kafelka obszaru. Mapa bitowa może być powtarzana w poziomie i w pionie, w oryginalnej orientacji lub alternatywnie przerzucana w poziomie i w pionie. Przerzucanie pozwala uniknąć przerw między kafelkami:
Metoda statyczna SKShader.CreateBitmap
tworząca ten moduł cieniowania ma SKBitmap
parametr i dwa elementy członkowskie SKShaderTileMode
wyliczenia:
public static SKShader CreateBitmap (SKBitmap src, SKShaderTileMode tmx, SKShaderTileMode tmy)
Dwa parametry wskazują tryby używane do układania kafelków poziomych i kafelków pionowych. Jest to to samo SKShaderTileMode
wyliczenie, które jest również używane z metodami gradientu.
Przeciążenie CreateBitmap
zawiera SKMatrix
argument umożliwiający wykonanie przekształcenia na kafelkach map bitowych:
public static SKShader CreateBitmap (SKBitmap src, SKShaderTileMode tmx, SKShaderTileMode tmy, SKMatrix localMatrix)
Ten artykuł zawiera kilka przykładów użycia tej transformacji macierzy z kafelkami map bitowych.
Eksplorowanie trybów kafelków
Pierwszy program w sekcji Tiling mapy bitowej na stronie Cieniowanie i inne efekty w przykładzie pokazuje efekty dwóch SKShaderTileMode
argumentów. Kafelek mapy bitowej przerzuca tryby XAML plik tworzy wystąpienie i SKCanvasView
dwa Picker
widoki, które umożliwiają wybranie SKShaderTilerMode
wartości dla kafelka poziomego i pionowego. Zwróć uwagę, że tablica elementów SKShaderTileMode
członkowskich jest zdefiniowana Resources
w sekcji:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:skia="clr-namespace:SkiaSharp;assembly=SkiaSharp"
xmlns:skiaforms="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
x:Class="SkiaSharpFormsDemos.Effects.BitmapTileFlipModesPage"
Title="Bitmap Tile Flip Modes">
<ContentPage.Resources>
<x:Array x:Key="tileModes"
Type="{x:Type skia:SKShaderTileMode}">
<x:Static Member="skia:SKShaderTileMode.Clamp" />
<x:Static Member="skia:SKShaderTileMode.Repeat" />
<x:Static Member="skia:SKShaderTileMode.Mirror" />
</x:Array>
</ContentPage.Resources>
<StackLayout>
<skiaforms:SKCanvasView x:Name="canvasView"
VerticalOptions="FillAndExpand"
PaintSurface="OnCanvasViewPaintSurface" />
<Picker x:Name="xModePicker"
Title="Tile X Mode"
Margin="10, 0"
ItemsSource="{StaticResource tileModes}"
SelectedIndex="0"
SelectedIndexChanged="OnPickerSelectedIndexChanged" />
<Picker x:Name="yModePicker"
Title="Tile Y Mode"
Margin="10, 10"
ItemsSource="{StaticResource tileModes}"
SelectedIndex="0"
SelectedIndexChanged="OnPickerSelectedIndexChanged" />
</StackLayout>
</ContentPage>
Konstruktor pliku za pomocą kodu ładuje się w zasobie mapy bitowej, który pokazuje małpę siedzącą. Najpierw przycina obraz przy użyciu ExtractSubset
metody SKBitmap
, aby głowa i stopy dotykały krawędzi mapy bitowej. Konstruktor następnie używa Resize
metody , aby utworzyć kolejną mapę bitową o połowę rozmiaru. Te zmiany sprawiają, że mapa bitowa jest nieco bardziej odpowiednia do układania:
public partial class BitmapTileFlipModesPage : ContentPage
{
SKBitmap bitmap;
public BitmapTileFlipModesPage ()
{
InitializeComponent ();
SKBitmap origBitmap = BitmapExtensions.LoadBitmapResource(
GetType(), "SkiaSharpFormsDemos.Media.SeatedMonkey.jpg");
// Define cropping rect
SKRectI cropRect = new SKRectI(5, 27, 296, 260);
// Get the cropped bitmap
SKBitmap croppedBitmap = new SKBitmap(cropRect.Width, cropRect.Height);
origBitmap.ExtractSubset(croppedBitmap, cropRect);
// Resize to half the width and height
SKImageInfo info = new SKImageInfo(cropRect.Width / 2, cropRect.Height / 2);
bitmap = croppedBitmap.Resize(info, SKBitmapResizeMethod.Box);
}
void OnPickerSelectedIndexChanged(object sender, EventArgs args)
{
canvasView.InvalidateSurface();
}
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
// Get tile modes from Pickers
SKShaderTileMode xTileMode =
(SKShaderTileMode)(xModePicker.SelectedIndex == -1 ?
0 : xModePicker.SelectedItem);
SKShaderTileMode yTileMode =
(SKShaderTileMode)(yModePicker.SelectedIndex == -1 ?
0 : yModePicker.SelectedItem);
using (SKPaint paint = new SKPaint())
{
paint.Shader = SKShader.CreateBitmap(bitmap, xTileMode, yTileMode);
canvas.DrawRect(info.Rect, paint);
}
}
}
Procedura PaintSurface
obsługi uzyskuje SKShaderTileMode
ustawienia z dwóch Picker
widoków i tworzy SKShader
obiekt na podstawie mapy bitowej i tych dwóch wartości. Ten cieniator służy do wypełniania kanwy:
Ekran systemu iOS po lewej stronie pokazuje efekt wartości domyślnych .SKShaderTileMode.Clamp
Mapa bitowa znajduje się w lewym górnym rogu. Poniżej mapy bitowej dolny wiersz pikseli jest powtarzany aż w dół. Po prawej stronie mapy bitowej po prawej stronie wielokrotnie powtarzana jest prawa kolumna pikseli. Pozostała część kanwy jest kolorowana ciemnym brązowym pikselem w prawym dolnym rogu mapy bitowej. Powinno być oczywiste, że Clamp
opcja prawie nigdy nie jest używana z tilingiem mapy bitowej!
Ekran systemu Android w środku pokazuje wynik SKShaderTileMode.Repeat
dla obu argumentów. Kafelek jest powtarzany w poziomie i w pionie. Na ekranie platforma uniwersalna systemu Windows jest wyświetlana wartość SKShaderTileMode.Mirror
. Kafelki są powtarzane, ale alternatywnie przerzucane w poziomie i w pionie. Zaletą tej opcji jest brak przerw między kafelkami.
Należy pamiętać, że można użyć różnych opcji dla powtórzeń poziomych i pionowych. Możesz określić SKShaderTileMode.Mirror
jako drugi argument, CreateBitmap
ale SKShaderTileMode.Repeat
jako trzeci argument. W każdym wierszu małpy nadal zmieniają się między normalnym obrazem a obrazem lustrzanym, ale żadna z małp nie jest do góry nogami.
Wzorzyste tła
Tiling mapy bitowej jest często używany do tworzenia wzorzystego tła na podstawie stosunkowo małej mapy bitowej. Klasycznym przykładem jest ściana z cegły.
Strona Algorithmic Brick Wall tworzy małą mapę bitową przypominającą całą cegłę i dwie połówki cegły oddzielonej moździerzem. Ponieważ ta cegła jest również używana w następnym przykładzie, jest tworzona przez konstruktor statyczny i upubliczniona z właściwością statyczną:
public class AlgorithmicBrickWallPage : ContentPage
{
static AlgorithmicBrickWallPage()
{
const int brickWidth = 64;
const int brickHeight = 24;
const int morterThickness = 6;
const int bitmapWidth = brickWidth + morterThickness;
const int bitmapHeight = 2 * (brickHeight + morterThickness);
SKBitmap bitmap = new SKBitmap(bitmapWidth, bitmapHeight);
using (SKCanvas canvas = new SKCanvas(bitmap))
using (SKPaint brickPaint = new SKPaint())
{
brickPaint.Color = new SKColor(0xB2, 0x22, 0x22);
canvas.Clear(new SKColor(0xF0, 0xEA, 0xD6));
canvas.DrawRect(new SKRect(morterThickness / 2,
morterThickness / 2,
morterThickness / 2 + brickWidth,
morterThickness / 2 + brickHeight),
brickPaint);
int ySecondBrick = 3 * morterThickness / 2 + brickHeight;
canvas.DrawRect(new SKRect(0,
ySecondBrick,
bitmapWidth / 2 - morterThickness / 2,
ySecondBrick + brickHeight),
brickPaint);
canvas.DrawRect(new SKRect(bitmapWidth / 2 + morterThickness / 2,
ySecondBrick,
bitmapWidth,
ySecondBrick + brickHeight),
brickPaint);
}
// Save as public property for other programs
BrickWallTile = bitmap;
}
public static SKBitmap BrickWallTile { private set; get; }
···
}
Wynikowa mapa bitowa ma szerokość 70 pikseli i 60 pikseli wysokości:
Pozostała część strony Algorytmiczna ściana ceglana tworzy SKShader
obiekt, który powtarza ten obraz w poziomie i w pionie:
public class AlgorithmicBrickWallPage : ContentPage
{
···
public AlgorithmicBrickWallPage ()
{
Title = "Algorithmic Brick Wall";
// Create SKCanvasView
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();
using (SKPaint paint = new SKPaint())
{
// Create bitmap tiling
paint.Shader = SKShader.CreateBitmap(BrickWallTile,
SKShaderTileMode.Repeat,
SKShaderTileMode.Repeat);
// Draw background
canvas.DrawRect(info.Rect, paint);
}
}
}
Oto wynik:
Możesz wolisz coś nieco bardziej realistycznego. W takim przypadku możesz zrobić zdjęcie rzeczywistej ściany ceglanej, a następnie przyciągnąć ją. Ta mapa bitowa ma szerokość 300 pikseli i 150 pikseli:
Ta mapa bitowa jest używana na stronie Ściana fotograficzna ceglana :
public class PhotographicBrickWallPage : ContentPage
{
SKBitmap bitmap = BitmapExtensions.LoadBitmapResource(
typeof(PhotographicBrickWallPage),
"SkiaSharpFormsDemos.Media.BrickWallTile.jpg");
public PhotographicBrickWallPage()
{
Title = "Photographic Brick Wall";
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();
using (SKPaint paint = new SKPaint())
{
// Create bitmap tiling
paint.Shader = SKShader.CreateBitmap(bitmap,
SKShaderTileMode.Mirror,
SKShaderTileMode.Mirror);
// Draw background
canvas.DrawRect(info.Rect, paint);
}
}
}
Zwróć uwagę, że SKShaderTileMode
argumenty , które CreateBitmap
mają być oba Mirror
. Ta opcja jest zwykle niezbędna w przypadku używania kafelków utworzonych na podstawie obrazów rzeczywistych. Dublowanie kafelków pozwala uniknąć przerw:
Niektóre prace są wymagane, aby uzyskać odpowiednią mapę bitową dla kafelka. Ten nie działa bardzo dobrze, ponieważ ciemniejszy cegła wyróżnia się zbyt wiele. Pojawia się regularnie w powtarzających się obrazach, ujawniając fakt, że ta ściana ceglana została zbudowana z mniejszej mapy bitowej.
Folder Media przykładu zawiera również ten obraz kamiennej ściany:
Jednak oryginalna mapa bitowa jest nieco za duża dla kafelka. Można go zmienić, ale SKShader.CreateBitmap
metoda może również zmienić rozmiar kafelka, stosując do niego przekształcenie. Ta opcja jest pokazana na stronie Kamienna ściana :
public class StoneWallPage : ContentPage
{
SKBitmap bitmap = BitmapExtensions.LoadBitmapResource(
typeof(StoneWallPage),
"SkiaSharpFormsDemos.Media.StoneWallTile.jpg");
public StoneWallPage()
{
Title = "Stone Wall";
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();
using (SKPaint paint = new SKPaint())
{
// Create scale transform
SKMatrix matrix = SKMatrix.MakeScale(0.5f, 0.5f);
// Create bitmap tiling
paint.Shader = SKShader.CreateBitmap(bitmap,
SKShaderTileMode.Mirror,
SKShaderTileMode.Mirror,
matrix);
// Draw background
canvas.DrawRect(info.Rect, paint);
}
}
}
Zostanie utworzona SKMatrix
wartość w celu skalowania obrazu do połowy oryginalnego rozmiaru:
Czy transformacja działa na oryginalnej mapie bitowej używanej w metodzie CreateBitmap
? Czy też przekształca wynikową tablicę kafelków?
Łatwym sposobem odpowiedzi na to pytanie jest dołączenie rotacji w ramach transformacji:
SKMatrix matrix = SKMatrix.MakeScale(0.5f, 0.5f);
SKMatrix.PostConcat(ref matrix, SKMatrix.MakeRotationDegrees(15));
Jeśli przekształcenie zostanie zastosowane do pojedynczego kafelka, każdy powtórzony obraz kafelka powinien zostać obrócony, a wynik będzie zawierać wiele przerw. Jednak na tym zrzucie ekranu widać, że złożona tablica kafelków została przekształcona:
W sekcji Wyrównanie kafelka zobaczysz przykład przekształcenia tłumaczenia zastosowanego do cieniowania.
Przykład symuluje tło ziarna drewna przy użyciu tilingu mapy bitowej na podstawie tej 240-pikselowej kwadratowej mapy bitowej:
To jest zdjęcie drewnianej podłogi. Opcja SKShaderTileMode.Mirror
pozwala na pojawienie się znacznie większego obszaru drewna:
Wyrównanie kafelka
Wszystkie pokazane do tej pory przykłady używały cieniowania utworzonego przez SKShader.CreateBitmap
program w celu pokrycia całej kanwy. W większości przypadków będziesz używać tilingu mapy bitowej do wypełniania mniejszych obszarów lub (rzadziej) do wypełniania wnętrz grubych linii. Oto fotograficzna ścianka z cegły używana do mniejszego prostokąta:
Może to wyglądać dobrze, a może nie. Być może jesteś zaniepokojony, że wzór tilinga nie zaczyna się od pełnej cegły w lewym górnym rogu prostokąta. Dzieje się tak dlatego, że cieniowania są wyrównane do kanwy, a nie z obiektem graficznym, który zdobią.
Poprawka jest prosta. SKMatrix
Utwórz wartość na podstawie przekształcenia tłumaczenia. Przekształcenie skutecznie przenosi wzorzec kafelków do punktu, w którym ma zostać wyrównany lewy górny róg kafelka. To podejście przedstawiono na stronie Wyrównanie kafelka, która utworzyła obraz nieprzygotowanych kafelków pokazanych powyżej:
public class TileAlignmentPage : ContentPage
{
bool isAligned;
public TileAlignmentPage()
{
Title = "Tile Alignment";
SKCanvasView canvasView = new SKCanvasView();
canvasView.PaintSurface += OnCanvasViewPaintSurface;
// Add tap handler
TapGestureRecognizer tap = new TapGestureRecognizer();
tap.Tapped += (sender, args) =>
{
isAligned ^= true;
canvasView.InvalidateSurface();
};
canvasView.GestureRecognizers.Add(tap);
Content = canvasView;
}
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
using (SKPaint paint = new SKPaint())
{
SKRect rect = new SKRect(info.Width / 7,
info.Height / 7,
6 * info.Width / 7,
6 * info.Height / 7);
// Get bitmap from other program
SKBitmap bitmap = AlgorithmicBrickWallPage.BrickWallTile;
// Create bitmap tiling
if (!isAligned)
{
paint.Shader = SKShader.CreateBitmap(bitmap,
SKShaderTileMode.Repeat,
SKShaderTileMode.Repeat);
}
else
{
SKMatrix matrix = SKMatrix.MakeTranslation(rect.Left, rect.Top);
paint.Shader = SKShader.CreateBitmap(bitmap,
SKShaderTileMode.Repeat,
SKShaderTileMode.Repeat,
matrix);
}
// Draw rectangle
canvas.DrawRect(rect, paint);
}
}
}
Strona Wyrównanie kafelka zawiera element TapGestureRecognizer
. Naciśnij lub kliknij ekran, a program przełącza się do SKShader.CreateBitmap
metody z argumentem SKMatrix
. Ta transformacja przesuwa wzorzec, tak aby lewy górny róg zawierał pełną cegłę:
Można również użyć tej techniki, aby upewnić się, że wyśrodkowany wzorzec mapy bitowej kafelków znajduje się w obszarze, który maluje. Na stronie Wyśrodkowane kafelki program obsługi najpierw oblicza współrzędne tak, PaintSurface
jakby wyświetlał pojedynczą mapę bitową w środku kanwy. Następnie używa tych współrzędnych do utworzenia przekształcenia translate dla SKShader.CreateBitmap
elementu . Ta transformacja przesuwa cały wzorzec, tak aby kafelek był wyśrodkowany:
public class CenteredTilesPage : ContentPage
{
SKBitmap bitmap = BitmapExtensions.LoadBitmapResource(
typeof(CenteredTilesPage),
"SkiaSharpFormsDemos.Media.monkey.png");
public CenteredTilesPage ()
{
Title = "Centered Tiles";
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();
// Find coordinates to center bitmap in canvas...
float x = (info.Width - bitmap.Width) / 2f;
float y = (info.Height - bitmap.Height) / 2f;
using (SKPaint paint = new SKPaint())
{
// ... but use them to create a translate transform
SKMatrix matrix = SKMatrix.MakeTranslation(x, y);
paint.Shader = SKShader.CreateBitmap(bitmap,
SKShaderTileMode.Repeat,
SKShaderTileMode.Repeat,
matrix);
// Use that tiled bitmap pattern to fill a circle
canvas.DrawCircle(info.Rect.MidX, info.Rect.MidY,
Math.Min(info.Width, info.Height) / 2,
paint);
}
}
}
Procedura PaintSurface
obsługi kończy się rysowaniem okręgu w środku kanwy. Na pewno jeden z kafelków znajduje się dokładnie w środku okręgu, a pozostałe są rozmieszczone w wzorcu symetrycznym:
Inne podejście skoncentrowane jest w rzeczywistości nieco łatwiejsze. Zamiast konstruować transformację tłumaczenia, która umieszcza kafelek w środku, można wyśrodkować róg wzorca kafelka. W wywołaniu SKMatrix.MakeTranslation
użyj argumentów dla środka kanwy:
SKMatrix matrix = SKMatrix.MakeTranslation(info.Rect.MidX, info.Rect.MidY);
Wzorzec jest nadal wyśrodkowany i symetryczny, ale żaden kafelek nie znajduje się w środku:
Uproszczenie poprzez rotację
Czasami użycie przekształcenia obracania w metodzie SKShader.CreateBitmap
może uprościć kafelek mapy bitowej. Staje się to oczywiste podczas próby zdefiniowania kafelka dla ogrodzenia łączącego łańcuch. Plik ChainLinkTile.cs tworzy tutaj kafelek (z różowym tłem na potrzeby jasności):
Kafelek musi zawierać dwa linki, aby kod dzielił kafelek na cztery ćwiartki. Ćwiartki w lewym górnym i prawym dolnym rogu są takie same, ale nie są kompletne. Przewody mają małe wycięcia, które muszą być obsługiwane z dodatkowym rysunkiem w prawym górnym i lewym dolnym rogu ćwiartki. Plik, który wykonuje całą tę pracę, to 174 wiersze długie.
Okazuje się, że tworzenie tego kafelka jest znacznie łatwiejsze:
Jeśli cieniator kafelków bitowych jest obracany o 90 stopni, wizualizacje są prawie takie same.
Kod umożliwiający utworzenie łatwiejszego kafelka linku łańcuchowego jest częścią strony Kafelek linku łańcuchowego. Konstruktor określa rozmiar kafelka na podstawie typu urządzenia, na którym działa program, a następnie wywołuje CreateChainLinkTile
metodę , która korzysta z mapy bitowej przy użyciu linii, ścieżek i cieniowań gradientu:
public class ChainLinkFencePage : ContentPage
{
···
SKBitmap tileBitmap;
public ChainLinkFencePage ()
{
Title = "Chain-Link Fence";
// Create bitmap for chain-link tiling
int tileSize = Device.Idiom == TargetIdiom.Desktop ? 64 : 128;
tileBitmap = CreateChainLinkTile(tileSize);
SKCanvasView canvasView = new SKCanvasView();
canvasView.PaintSurface += OnCanvasViewPaintSurface;
Content = canvasView;
}
SKBitmap CreateChainLinkTile(int tileSize)
{
tileBitmap = new SKBitmap(tileSize, tileSize);
float wireThickness = tileSize / 12f;
using (SKCanvas canvas = new SKCanvas(tileBitmap))
using (SKPaint paint = new SKPaint())
{
canvas.Clear();
paint.Style = SKPaintStyle.Stroke;
paint.StrokeWidth = wireThickness;
paint.IsAntialias = true;
// Draw straight wires first
paint.Shader = SKShader.CreateLinearGradient(new SKPoint(0, 0),
new SKPoint(0, tileSize),
new SKColor[] { SKColors.Silver, SKColors.Black },
new float[] { 0.4f, 0.6f },
SKShaderTileMode.Clamp);
canvas.DrawLine(0, tileSize / 2,
tileSize / 2, tileSize / 2 - wireThickness / 2, paint);
canvas.DrawLine(tileSize, tileSize / 2,
tileSize / 2, tileSize / 2 + wireThickness / 2, paint);
// Draw curved wires
using (SKPath path = new SKPath())
{
path.MoveTo(tileSize / 2, 0);
path.LineTo(tileSize / 2 - wireThickness / 2, tileSize / 2);
path.ArcTo(wireThickness / 2, wireThickness / 2,
0,
SKPathArcSize.Small,
SKPathDirection.CounterClockwise,
tileSize / 2, tileSize / 2 + wireThickness / 2);
paint.Shader = SKShader.CreateLinearGradient(new SKPoint(0, 0),
new SKPoint(0, tileSize),
new SKColor[] { SKColors.Silver, SKColors.Black },
null,
SKShaderTileMode.Clamp);
canvas.DrawPath(path, paint);
path.Reset();
path.MoveTo(tileSize / 2, tileSize);
path.LineTo(tileSize / 2 + wireThickness / 2, tileSize / 2);
path.ArcTo(wireThickness / 2, wireThickness / 2,
0,
SKPathArcSize.Small,
SKPathDirection.CounterClockwise,
tileSize / 2, tileSize / 2 - wireThickness / 2);
paint.Shader = SKShader.CreateLinearGradient(new SKPoint(0, 0),
new SKPoint(0, tileSize),
new SKColor[] { SKColors.White, SKColors.Silver },
null,
SKShaderTileMode.Clamp);
canvas.DrawPath(path, paint);
}
return tileBitmap;
}
}
···
}
Z wyjątkiem przewodów, kafelek jest przezroczysty, co oznacza, że można go wyświetlić na wierzchu czegoś innego. Program ładuje się w jednym z zasobów mapy bitowej, wyświetla go, aby wypełnić kanwę, a następnie rysuje cieniowanie na górze:
public class ChainLinkFencePage : ContentPage
{
SKBitmap monkeyBitmap = BitmapExtensions.LoadBitmapResource(
typeof(ChainLinkFencePage), "SkiaSharpFormsDemos.Media.SeatedMonkey.jpg");
···
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.UniformToFill,
BitmapAlignment.Center, BitmapAlignment.Start);
using (SKPaint paint = new SKPaint())
{
paint.Shader = SKShader.CreateBitmap(tileBitmap,
SKShaderTileMode.Repeat,
SKShaderTileMode.Repeat,
SKMatrix.MakeRotationDegrees(45));
canvas.DrawRect(info.Rect, paint);
}
}
}
Zwróć uwagę, że cieniowanie jest obracane o 45 stopni, więc jest zorientowane jak prawdziwe ogrodzenie linku łańcucha:
Animowanie kafelków mapy bitowej
Możesz animować cały wzorzec kafelka mapy bitowej, animując transformację macierzy. Być może chcesz, aby wzorzec poruszał się w poziomie lub w pionie lub w obu przypadkach. Można to zrobić, tworząc przekształcenie tłumaczenia na podstawie współrzędnych przesuwających.
Istnieje również możliwość narysowania na małej mapie bitowej lub manipulowania bitami pikseli mapy bitowej w tempie 60 razy na sekundę. Ta mapa bitowa może być następnie używana do układania płytek, a cały wzór kafelków może być animowany.
Na stronie Animowany kafelek mapy bitowej przedstawiono to podejście. Mapa bitowa jest tworzone jako pole o powierzchni 64 pikseli. Konstruktor wywołuje metodę DrawBitmap
, aby nadać jej początkowy wygląd. angle
Jeśli pole ma wartość zero (jak to jest, gdy metoda jest wywoływana po raz pierwszy), mapa bitowa zawiera dwa linie skrzyżowane jako X. Linie są wystarczająco długie, aby zawsze docierać do krawędzi mapy bitowej niezależnie od angle
wartości:
public class AnimatedBitmapTilePage : ContentPage
{
const int SIZE = 64;
SKCanvasView canvasView;
SKBitmap bitmap = new SKBitmap(SIZE, SIZE);
float angle;
···
public AnimatedBitmapTilePage ()
{
Title = "Animated Bitmap Tile";
// Initialize bitmap prior to animation
DrawBitmap();
// Create SKCanvasView
canvasView = new SKCanvasView();
canvasView.PaintSurface += OnCanvasViewPaintSurface;
Content = canvasView;
}
···
void DrawBitmap()
{
using (SKCanvas canvas = new SKCanvas(bitmap))
using (SKPaint paint = new SKPaint())
{
paint.Style = SKPaintStyle.Stroke;
paint.Color = SKColors.Blue;
paint.StrokeWidth = SIZE / 8;
canvas.Clear();
canvas.Translate(SIZE / 2, SIZE / 2);
canvas.RotateDegrees(angle);
canvas.DrawLine(-SIZE, -SIZE, SIZE, SIZE, paint);
canvas.DrawLine(-SIZE, SIZE, SIZE, -SIZE, paint);
}
}
···
}
Obciążenie animacji występuje w przesłonięciach OnAppearing
i OnDisappearing
. Metoda OnTimerTick
animuje angle
wartość z zakresu od 0 stopni do 360 stopni co 10 sekund, aby obrócić figurę X w mapie bitowej:
public class AnimatedBitmapTilePage : ContentPage
{
···
// For animation
bool isAnimating;
Stopwatch stopwatch = new Stopwatch();
···
protected override void OnAppearing()
{
base.OnAppearing();
isAnimating = true;
stopwatch.Start();
Device.StartTimer(TimeSpan.FromMilliseconds(16), OnTimerTick);
}
protected override void OnDisappearing()
{
base.OnDisappearing();
stopwatch.Stop();
isAnimating = false;
}
bool OnTimerTick()
{
const int duration = 10; // seconds
angle = (float)(360f * (stopwatch.Elapsed.TotalSeconds % duration) / duration);
DrawBitmap();
canvasView.InvalidateSurface();
return isAnimating;
}
···
}
Ze względu na symetrię rysunku X jest to samo co obracanie angle
wartości z 0 stopni do 90 stopni co 2,5 sekundy.
Procedura PaintSurface
obsługi tworzy cieniowanie na podstawie mapy bitowej i używa obiektu farby do kolorowania całej kanwy:
public class AnimatedBitmapTilePage : ContentPage
{
···
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
using (SKPaint paint = new SKPaint())
{
paint.Shader = SKShader.CreateBitmap(bitmap,
SKShaderTileMode.Mirror,
SKShaderTileMode.Mirror);
canvas.DrawRect(info.Rect, paint);
}
}
}
Opcje SKShaderTileMode.Mirror
zapewniają, że ramiona X w każdej mapie bitowej łączą się z X w sąsiednich mapach bitowych, aby utworzyć ogólny animowany wzorzec, który wydaje się znacznie bardziej złożony, niż sugeruje prosta animacja: