Sdílet prostřednictvím


Rastrová mapa SkiaSharp

Jak jste viděli v předchozích dvou článcích, SKShader třída může vytvořit lineární nebo kruhové přechody. Tento článek se zaměřuje na SKShader objekt, který používá bitmapu k dlaždici oblasti. Rastrový obrázek lze opakovat vodorovně a svisle, buď v původní orientaci, nebo střídavě překlopit vodorovně a svisle. Překlopení zabraňuje přerušení mezi dlaždicemi:

Rastrový graf – ukázka pro vázání

Statická SKShader.CreateBitmap metoda, která vytvoří tento shader, má SKBitmap parametr a dva členy výčtu SKShaderTileMode :

public static SKShader CreateBitmap (SKBitmap src, SKShaderTileMode tmx, SKShaderTileMode tmy)

Dva parametry označují režimy používané pro vodorovné provazování a svislé provazování. Jedná se o stejný SKShaderTileMode výčet, který se používá také s metodami přechodu.

Přetížení CreateBitmap obsahuje SKMatrix argument k provedení transformace na dlaždicových bitmapách:

public static SKShader CreateBitmap (SKBitmap src, SKShaderTileMode tmx, SKShaderTileMode tmy, SKMatrix localMatrix)

Tento článek obsahuje několik příkladů použití této maticové transformace s dlaždicemi bitmap.

Prozkoumání režimů dlaždic

První program v části Rastrové vazby shaderů a dalších efektů stránky ukázky ukazuje efekty těchto dvou SKShaderTileMode argumentů. Soubor XAML v režimu překlopení rastrových dlaždic vytvoří SKCanvasView instanci a dvě Picker zobrazení, která umožňují vybrat SKShaderTilerMode hodnotu pro vodorovné a svislé provazování. Všimněte si, že pole SKShaderTileMode členů je definováno v oddílu Resources :

<?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 souboru s kódem se načte v rastrovém prostředku, který zobrazuje opici sedící. Nejprve oříří obrázek metodou ExtractSubset SKBitmap tak, aby se hlava a nohy dotkly okrajů rastrového obrázku. Konstruktor pak použije metodu Resize k vytvoření další bitmapy s poloviční velikostí. Tyto změny dělají rastrový obrázek trochu vhodnější pro provazování:

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

Obslužná PaintSurface rutina SKShaderTileMode získá nastavení ze dvou Picker zobrazení a vytvoří SKShader objekt založený na rastrovém obrázku a těchto dvou hodnotách. Tento shader slouží k vyplnění plátna:

Režimy překlopení rastrových dlaždic

Obrazovka iOS na levé straně zobrazuje efekt výchozích hodnot SKShaderTileMode.Clamp. Rastrový obrázek se nachází v levém horním rohu. Pod rastrem se dolní řádek pixelů opakuje až dolů. Napravo od rastrového obrázku se úplně vpravo opakuje sloupec pixelů úplně vpravo. Zbytek plátna je barevný tmavě hnědý pixel v pravém dolním rohu rastrového obrázku. Mělo by být zřejmé, že možnost Clamp se téměř nikdy nepoužívá s rastrovým tilingem!

Na obrazovce Androidu uprostřed se zobrazí výsledek SKShaderTileMode.Repeat obou argumentů. Dlaždice se opakuje vodorovně a svisle. Na obrazovce Univerzální platforma Windows se zobrazí SKShaderTileMode.Mirror. Dlaždice se opakují, ale střídavě se překlopí vodorovně a svisle. Výhodou této možnosti je, že mezi dlaždicemi nejsou žádné přerušení.

Mějte na paměti, že pro vodorovné a svislé opakování můžete použít různé možnosti. Jako druhý argument CreateBitmap můžete zadatSKShaderTileMode.Mirror, ale SKShaderTileMode.Repeat jako třetí argument. Na každém řádku se opice stále střídají mezi normálním obrazem a zrcadlovým obrazem, ale žádná z opiček není vzhůru nohama.

Vzorovaná pozadí

Rastrové vazby se běžně používají k vytvoření vzorového pozadí z relativně malého rastrového obrázku. Klasickým příkladem je cihlová stěna.

Stránka Algorithmic Brick Wall vytvoří malý rastrový obrázek, který se podobá celé cihlě a dvě poloviny cihly oddělené maltou. Vzhledem k tomu, že se tato cihla používá i v další ukázce, je vytvořená statickým konstruktorem a zpřístupněna se statickou vlastností:

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

Výsledný rastrový obrázek je široký 70 pixelů a vysoký 60 pixelů:

Algoritmická cihlová zeď dlaždice

Zbytek stránky Algorithmic Brick Wall vytvoří SKShader objekt, který opakuje tento obrázek vodorovně a svisle:

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

Tady je výsledek:

Algoritmická cihlová stěna

Možná dáváte přednost něčemu trochu realističtějšímu. V takovém případě můžete pořídit fotografii skutečné cihlové zdi a pak ji oříznout. Tento rastrový obrázek je široký 300 pixelů a vysoký 150 pixelů:

Cihlová stěna dlaždice

Tento rastrový obrázek se používá na stránce Fotografická cihlová stěna :

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

Všimněte si, že SKShaderTileMode argumenty, které mají CreateBitmap být oba Mirror. Tato možnost je obvykle nutná při použití dlaždic vytvořených z obrázků z reálného světa. Zrcadlení dlaždic zabraňuje přerušení:

Fotografická cihlová stěna

K získání vhodného rastrového obrázku pro dlaždici se vyžaduje určitá práce. Tohle nefunguje moc dobře, protože tmavší cihla příliš vynikne. Vypadá pravidelně v opakovaných obrázcích a ukazuje skutečnost, že tato cihlová stěna byla vytvořena z menší bitmapy.

Složka Média ukázky obsahuje také tento obrázek kamenné zdi:

Kamenná stěna dlaždice

Původní rastrový obrázek je ale pro dlaždici trochu příliš velký. Velikost může být změněna, ale SKShader.CreateBitmap metoda může také změnit velikost dlaždice použitím transformace na dlaždici. Tato možnost je ukázaná na stránce Kamenná stěna :

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

Vytvoří se SKMatrix hodnota, která škáluje obrázek na polovinu původní velikosti:

Kamenná stěna

Funguje transformace na původním rastrovém obrázku použitém CreateBitmap v metodě? Nebo transformuje výsledné pole dlaždic?

Snadným způsobem, jak na tuto otázku odpovědět, je zahrnout rotaci jako součást transformace:

SKMatrix matrix = SKMatrix.MakeScale(0.5f, 0.5f);
SKMatrix.PostConcat(ref matrix, SKMatrix.MakeRotationDegrees(15));

Pokud se transformace použije na jednotlivé dlaždici, měl by se každý opakovaný obrázek dlaždice otočit a výsledek by obsahoval mnoho přerušení. Na tomto snímku obrazovky je ale zřejmé, že se transformuje složené pole dlaždic:

Kamenná stěna otočena

V oddílu Zarovnání dlaždice uvidíte příklad transformace překladu použité na shader.

Ukázka simuluje pozadí ze dřeva pomocí rastrového provazování na základě tohoto čtvercového rastrového obrázku o rozměrech 240 pixelů:

Dřevo zrno

To je fotografie dřevěné podlahy. Tato SKShaderTileMode.Mirror možnost umožňuje, aby se zobrazovala jako mnohem větší plocha dřeva:

Cat Clock

Zarovnání dlaždic

Všechny dosud zobrazené příklady používaly shader vytvořený SKShader.CreateBitmap k pokrytí celého plátna. Ve většině případů budete pro vyplňování menších oblastí nebo (zřídka) pro vyplňování interiérů silných čar používat rastrové vazby. Zde je fotografická cihlová dlaždice použitá pro menší obdélník:

Zarovnání dlaždic

To by pro vás mohlo vypadat dobře, nebo možná ne. Možná jste vyrušováni, že vzor provazování nezačíná plnou cihlou v levém horním rohu obdélníku. Je to proto, že shadery jsou zarovnané s plátnem a ne grafickým objektem, který je ozdobil.

Oprava je jednoduchá. Vytvořte SKMatrix hodnotu na základě transformace překladu. Transformace efektivně posune dlaždicový vzor na místo, kde chcete, aby byl levý horní roh dlaždice zarovnaný. Tento přístup je ukázaný na stránce Zarovnání dlaždic, která vytvořila obrázek nerovnaných dlaždic zobrazených výše:

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

Stránka Zarovnání dlaždice obsahuje .TapGestureRecognizer Klepněte nebo klikněte na obrazovku a program se přepne na metodu SKShader.CreateBitmap s argumentem SKMatrix . Tato transformace posune vzor tak, aby levý horní roh obsahoval plnou cihlu:

Zarovnání dlaždic, na které bylo klepnutí

Pomocí této techniky můžete také zajistit, aby byl vzor rastrového obrázku na střed v oblasti, ve které maluje. Na stránce Na střed dlaždic obslužná rutina nejprve vypočítá souřadnice, PaintSurface jako by zobrazovala jeden rastrový obrázek uprostřed plátna. Tyto souřadnice pak použije k vytvoření transformace překladu pro SKShader.CreateBitmap. Tato transformace posune celý vzor tak, aby byla dlaždice na střed:

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

Obslužná rutina PaintSurface se uzavírá nakreslením kruhu uprostřed plátna. Je jisté, že jedna z dlaždic je přesně ve středu kruhu a ostatní jsou uspořádány v symetrických vzorech:

Zacentrované dlaždice

Další přístup na střed je ve skutečnosti o něco jednodušší. Místo vytvoření transformace přeložit, která umístí dlaždici do středu, můžete zarovnát roh dlaždicového vzoru. SKMatrix.MakeTranslation Ve volání použijte argumenty pro střed plátna:

SKMatrix matrix = SKMatrix.MakeTranslation(info.Rect.MidX, info.Rect.MidY);

Vzor je stále na střed a symetrický, ale žádná dlaždice není uprostřed:

Alternativní dlaždice na střed

Zjednodušení prostřednictvím obměně

Někdy použití transformace otočení v SKShader.CreateBitmap metodě může zjednodušit rastrovou dlaždici. To se projeví při pokusu o definování dlaždice pro plot s řetězovým propojením. Soubor ChainLinkTile.cs vytvoří dlaždici zobrazenou zde (s růžovým pozadím pro účely přehlednosti):

Dlaždice s pevnou vazbou řetězu

Dlaždice musí obsahovat dvě propojení, aby kód rozdělil dlaždici na čtyři kvadranty. Levé horní a pravé dolní kvadranty jsou stejné, ale nedokončí se. Dráty mají malé zářezy, které musí být zpracovány s další kresbou v pravém horním a levém dolním kvadrantu. Soubor, který tuto práci provede, je dlouhý 174 řádků.

Ukázalo se, že vytvoření této dlaždice je mnohem jednodušší:

Jednodušší dlaždice s propojením řetězu

Pokud je shader rastrové dlaždice otočený o 90 stupňů, vizuály jsou téměř stejné.

Kód pro vytvoření jednodušší dlaždice řetězového propojení je součástí stránky Dlaždice řetězu. Konstruktor určuje velikost dlaždice na základě typu zařízení, na kterém je program spuštěn, a potom volá CreateChainLinkTile, který nakreslí rastrový obrázek pomocí čar, cest a přechodových shaderů:

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

Kromě drátů je dlaždice průhledná, což znamená, že ji můžete zobrazit nad něčím jiným. Program se načte v jednom z rastrových prostředků, zobrazí ho k vyplnění plátna a potom nakreslí shader na vrcholu:

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

Všimněte si, že shader je otočený o 45 stupňů, takže je orientovaný jako skutečný plot řetězového propojení:

Řetězový plot

Animace rastrových dlaždic

Animací celého vzoru rastrové dlaždice můžete animací transformace matice. Možná chcete, aby se vzor přesunul vodorovně nebo svisle nebo obojí. Můžete to udělat tak, že vytvoříte transformaci překladu na základě souřadnic posunu.

Je také možné kreslit na malý rastrový obrázek nebo manipulovat s bity pixelů rastrového obrázku rychlostí 60krát za sekundu. Tento rastrový obrázek se pak dá použít pro provazování a celý dlaždicový vzor se může zdát animovaný.

Tento přístup ukazuje stránka Animované rastrové dlaždice . Rastrový obrázek se vytvoří jako pole, které má být 64 pixelů čtvercové. Konstruktor volá DrawBitmap , aby mu dal počáteční vzhled. angle Pokud je pole nula (jak je při prvním zavolání metody), pak rastrový obrázek obsahuje dva řádky křížené jako X. Čáry jsou dostatečně dlouhé, aby se vždy dostaly k okraji rastrového obrázku bez angle ohledu na hodnotu:

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

Režie animace se provádí v OnAppearing přepsání.OnDisappearing Metoda OnTimerTick animuje angle hodnotu od 0 stupňů do 360 stupňů každých 10 sekund a otočí obrázek X v rastrovém obrázku:

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

Vzhledem k symetrii obrázku X je to stejné jako otočení angle hodnoty od 0 stupňů do 90 stupňů každých 2,5 sekund.

Obslužná rutina PaintSurface vytvoří shader z rastrového obrázku a použije objekt malování k obarvení celého plátna:

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

Možnosti SKShaderTileMode.Mirror zajišťují, aby se zbraně X v každém rastrovém obrázku spojily s X v sousedních bitmapách a vytvořily celkový animovaný vzor, který vypadá mnohem složitější než jednoduchá animace by navrhla:

Animovaná bitmapová dlaždice