SkiaSharp bit eşlem döşeme

Önceki iki makalede gördüğünüz gibi, SKShader sınıf doğrusal veya döngüsel gradyanlar oluşturabilir. Bu makalede, bir alanı döşemek için bit eşlem kullanan nesneye odaklanılır SKShader . Bit eşlem, özgün yönlendirmesinde yatay ve dikey olarak yinelenebilir veya alternatif olarak yatay ve dikey olarak çevrilebilir. Çevirme, kutucuklar arasındaki kesintileri önler:

Bit Eşlem Döşeme Örneği

Bu gölgelendiriciyi oluşturan statik SKShader.CreateBitmap yöntemin bir SKBitmap parametresi ve numaralandırmanın SKShaderTileMode iki üyesi vardır:

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

İki parametre, yatay döşeme ve dikey döşeme için kullanılan modları gösterir. Bu, SKShaderTileMode gradyan yöntemleriyle de kullanılan sabit listesidir.

Aşırı CreateBitmap yükleme, kutucuklu bit eşlemlerde dönüşüm gerçekleştirmek için bir SKMatrix bağımsız değişken içerir:

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

Bu makale, bu matris dönüşümünü kutucuklu bit eşlemlerle kullanmaya yönelik çeşitli örnekler içerir.

Kutucuk modlarını keşfetme

Örneğin Gölgelendiriciler ve diğer Efektler sayfasının Bit Eşlem Döşemesi bölümündeki ilk program, iki SKShaderTileMode bağımsız değişkenin etkilerini gösterir. Bit Eşlem Kutucuğu Çevirme Modları XAML dosyası, yatay ve dikey döşeme için bir değer seçmenize olanak sağlayan bir SKShaderTilerMode ve iki Picker görünümün örneğini SKCanvasView oluşturur. Üyelerin bir dizisinin SKShaderTileMode bölümünde tanımlandığına Resources dikkat edin:

<?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>

Arka planda kod dosyasının oluşturucusunun, bir maymunun oturduğunu gösteren bit eşlem kaynağına yüklenmesi. Önce yöntemini SKBitmap kullanarak ExtractSubset görüntüyü kırparak baş ve ayakların bit eşlem kenarlarına dokunmasını sağlar. Oluşturucu daha sonra yöntemini kullanarak boyutunun Resize yarısına sahip başka bir bit eşlem oluşturur. Bu değişiklikler bit eşlemi döşeme için biraz daha uygun hale getirir:

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

İşleyici, PaintSurface ayarları iki Picker görünümden alır SKShaderTileMode ve bit eşlem ile bu iki değeri temel alan bir SKShader nesne oluşturur. Bu gölgelendirici tuvali doldurmak için kullanılır:

Bit Eşlem Kutucuğu Çevirme Modları

Soldaki iOS ekranı varsayılan değerlerinin SKShaderTileMode.Clampetkisini gösterir. Bit eşlem sol üst köşede yer alır. Bit eşlem altında, piksellerin alt satırı aşağı doğru yinelenir. Bit eşlemin sağındaki en sağdaki piksel sütunu, tüm yol boyunca yinelenir. Tuvalin geri kalanı bit eşleminin sağ alt köşesindeki koyu kahverengi pikselle renklendirilir. Seçeneğin Clamp bit eşlem döşemesi ile neredeyse hiç kullanılmadığının açık olması gerekir!

Ortadaki Android ekranı, her iki bağımsız değişkenin SKShaderTileMode.Repeat sonucunu gösterir. Kutucuk yatay ve dikey olarak yinelenir. Evrensel Windows Platformu ekranında gösterilirSKShaderTileMode.Mirror. Kutucuklar yinelenir, ancak yatay ve dikey olarak alternatif olarak çevrilmiş olur. Bu seçeneğin avantajı, kutucuklar arasında kesinti olmamasıdır.

Yatay ve dikey yineleme için farklı seçenekler kullanabileceğinizi unutmayın. öğesinin ikinci bağımsız değişkeni olarak ancak SKShaderTileMode.Repeat üçüncü bağımsız değişken CreateBitmap olarak belirtebilirsinizSKShaderTileMode.Mirror. Her satırda, maymunlar normal görüntü ile ayna görüntüsü arasında hala alternatiftir, ancak maymunların hiçbiri baş aşağı değil.

Desenli arka planlar

Bit eşlem döşemesi genellikle nispeten küçük bir bit eşlemden desenli arka plan oluşturmak için kullanılır. Klasik örnek bir tuğla duvardır.

Algoritmik Tuğla Duvar sayfası, bir bütün tuğlaya benzeyen küçük bir bit eşlem ve harçla ayrılmış bir tuğlanın iki yarısı oluşturur. Bu tuğla bir sonraki örnekte de kullanıldığından, statik bir oluşturucu tarafından oluşturulur ve statik bir özellik ile genel kullanıma sunar:

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

Sonuçta elde edilen bit eşlem 70 piksel genişliğinde ve 60 piksel yüksekliğindedir:

Algoritmik Tuğla Duvar Kutucuğu

Algoritmik Tuğla Duvar sayfasının geri kalanı, bu görüntüyü yatay ve dikey olarak tekrarlayan bir SKShader nesne oluşturur:

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

Sonuç şu şekildedir:

Algoritmik Tuğla Duvar

Biraz daha gerçekçi bir şey tercih edebilirsiniz. Bu durumda, gerçek bir tuğla duvarın fotoğrafını çekebilir ve sonra kırpabilirsiniz. Bu bit eşlem 300 piksel genişliğinde ve 150 piksel yüksekliğindedir:

Tuğla Duvar Kiremit

Bu bit eşlem, FotoğrafIk Tuğla Duvar sayfasında kullanılır:

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

bağımsız değişkenlerinin SKShaderTileMode CreateBitmap her ikisi de Mirrorolduğuna dikkat edin. Bu seçenek genellikle gerçek dünya görüntülerinden oluşturulan kutucukları kullandığınızda gereklidir. Kutucukların yansıtılması kesintileri önler:

Fotoğrafik Tuğla Duvar

Kutucuk için uygun bir bit eşlem elde etmek için bazı çalışmalar gereklidir. Bu çok iyi çalışmıyor çünkü koyu tuğla çok fazla göze çarpıyor. Tekrarlanan görüntülerde düzenli olarak görünür ve bu tuğla duvarın daha küçük bir bit eşlemden oluşturulduğu gerçeği ortaya çıkar.

Örneğin Media klasörü, taş bir duvarın bu görüntüsünü de içerir:

Taş Duvar Kutucuğu

Ancak, özgün bit eşlem bir kutucuk için biraz fazla büyük. Yeniden boyutlandırılabilir, ancak SKShader.CreateBitmap yöntemi kutucuğa bir dönüşüm uygulayarak kutucuğu yeniden boyutlandırabilir. Bu seçenek Taş Duvar sayfasında gösterilmiştir:

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

SKMatrix Görüntüyü özgün boyutunun yarısına ölçeklendirmek için bir değer oluşturulur:

Taş Duvar

Dönüştürme yönteminde kullanılan özgün bit eşlem üzerinde CreateBitmap çalışır mı? Yoksa sonuçta elde edilen kutucuk dizisini mi dönüştürüyor?

Bu soruyu yanıtlamanın kolay bir yolu, dönüşümün bir parçası olarak döndürme eklemektir:

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

Dönüşüm tek tek kutucuğa uygulanırsa, kutucuğun yinelenen her görüntüsü döndürülmelidir ve sonuç birçok kesinti içerir. Ancak bu ekran görüntüsünde bileşik kutucuk dizisinin dönüştürüldüğü açıktır:

Taş Duvar Döndürüldü

Kutucuk hizalama bölümünde gölgelendiriciye uygulanan bir çeviri dönüştürme örneği göreceksiniz.

Örnek, bu 240 piksel kare bit eşlem temelinde bit eşlem döşemesi kullanarak ahşap taneli arka plan benzetimi sağlar:

Ahşap Tanecik

Bu bir ahşap zemin fotoğrafı. seçeneği SKShaderTileMode.Mirror , çok daha büyük bir ahşap alanı olarak görünmesini sağlar:

Kedi Saati

Kutucuk hizalaması

Şimdiye kadar gösterilen tüm örnekler, tuvalin tamamını kapsayacak şekilde tarafından SKShader.CreateBitmap oluşturulan gölgelendiriciyi kullanmıştır. Çoğu durumda, daha küçük alanları dosyalama için bit eşlem döşemesi veya kalın çizgilerin iç kısımlarını doldurmak için (daha nadiren) kullanacaksınız. Daha küçük bir dikdörtgen için kullanılan fotoğrafik tuğla duvar kutucuğu aşağıdadır:

Kutucuk Hizalama

Bu size iyi görünebilir ya da olmayabilir. Belki de döşeme deseninin dikdörtgenin sol üst köşesindeki dolu bir tuğlayla başlamaması sizi rahatsız ediyor. Bunun nedeni gölgelendiricilerin süsledikleri grafik nesneyle değil tuvalle hizalanmış olmasıdır.

Düzeltme basittir. SKMatrix Çeviri dönüşümünü temel alan bir değer oluşturun. Dönüşüm, kutucuklu deseni, kutucuğun sol üst köşesinin hizalanmasını istediğiniz noktaya etkili bir şekilde kaydırır. Bu yaklaşım, yukarıda gösterilen hizalanmamış kutucukların görüntüsünü oluşturan Kutucuk Hizalama sayfasında gösterilmiştir:

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

Kutucuk Hizalama sayfası bir TapGestureRecognizeriçerir. Ekrana dokunun veya tıklayın; program bir SKMatrix bağımsız değişkenle yönteme SKShader.CreateBitmap geçer. Bu dönüşüm, deseni kaydırır, böylece sol üst köşe tam tuğla içerir:

Kutucuk Hizalaması Eşlendi

Kutucuklu bit eşlem deseninin boyanacak alan içinde ortalandığından emin olmak için de bu tekniği kullanabilirsiniz. Ortalanmış Kutucuklar sayfasında, PaintSurface işleyici önce koordinatları tuvalin ortasında tek bit eşlemi görüntüleyecekmiş gibi hesaplar. Ardından bu koordinatları kullanarak için SKShader.CreateBitmapbir çeviri dönüşümü oluşturur. Bu dönüşüm, bir kutucuğun ortalanmış olması için desenin tamamını kaydırır:

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

İşleyici, PaintSurface tuvalin ortasına bir daire çizerek sonuç alır. Elbette, kutucuklardan biri dairenin tam ortasındadır ve diğerleri simetrik bir düzende düzenlenmiştir:

Ortalanmış Kutucuklar

Başka bir merkezleme yaklaşımı aslında biraz daha kolaydır. Kutucuğu ortaya koyan bir çeviri dönüşümü oluşturmak yerine, kutucuklu desenin bir köşesini ortalayabilirsiniz. Çağrısında SKMatrix.MakeTranslation , tuvalin merkezi için bağımsız değişkenleri kullanın:

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

Desen yine ortalanmış ve simetriktir, ancak ortada hiçbir kutucuk yoktur:

Ortalanmış Kutucuklar Alternatif

Döndürme yoluyla basitleştirme

Bazen yönteminde SKShader.CreateBitmap döndürme dönüşümü kullanmak bit eşlem kutucuğunu basitleştirebilir. Zincir bağlantı çiti için bir kutucuk tanımlamaya çalışırken bu durum ortaya çıkıyor. ChainLinkTile.cs dosyası burada gösterilen kutucuğu oluşturur (netlik amacıyla pembe arka plan ile):

Sabit zincir bağlantısı kutucuğu

Kutucuğun iki bağlantı içermesi gerekir, böylece kod kutucuğu dört çeyrekte böler. Sol üst ve sağ alt kadranlar aynıdır, ancak tam değildir. Tellerin sağ üst ve sol alt kadranlarında ek çizimlerle işlenmesi gereken küçük çentikler vardır. Tüm bu işi yapar dosya 174 satır uzunluğundadır.

Bu kutucuğu oluşturmak çok daha kolay hale gelecektir:

Daha kolay zincir bağlantısı kutucuğu

Bit eşlem kutucuğu gölgelendiricisi 90 derece döndürülürse görseller neredeyse aynıdır.

Daha kolay zincir bağlantısı kutucuğunu oluşturma kodu, Zincir Bağlantı Kutucuğu sayfasının bir parçasıdır. Oluşturucu, programın üzerinde çalıştığı cihaz türüne göre bir kutucuk boyutu belirler ve ardından bit eşlem üzerinde çizgiler, yollar ve gradyan gölgelendiriciler kullanarak çizen öğesini çağırır CreateChainLinkTile:

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

Kablolar dışında kutucuk saydamdır ve başka bir şeyin üzerinde görüntüleyebileceğiniz anlamına gelir. Program bit eşlem kaynaklarından birine yüklenir, tuvali doldurmak için görüntüler ve gölgelendiriciyi üzerine çizer:

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

Gölgelendiricinin 45 derece döndürüldüğünü, bu nedenle gerçek bir zincir bağlantı çiti gibi yönlendirildiğini görebilirsiniz:

Zincir Bağlantı Çiti

Bit eşlem kutucuklarını animasyon ekleme

Matris dönüşümüne animasyon uygulayarak bit eşlem kutucuğu deseninin tamamına animasyon uygulayabilirsiniz. Belki de desenin yatay veya dikey olarak ya da her ikisini birden hareket etmesini istiyorsunuz. Bunu yapmak için, kaydırma koordinatlarını temel alan bir çeviri dönüşümü oluşturabilirsiniz.

Küçük bir bit eşlem üzerinde çizim yapmak veya bit eşlem piksel bitlerini saniyede 60 kez işlemek de mümkündür. Bu bit eşlem daha sonra döşeme için kullanılabilir ve kutucuklu desenin tamamı animasyonlu gibi görünebilir.

Animasyonlu Bit Eşlem Kutucuğu sayfası bu yaklaşımı gösterir. Bit eşlem, 64 piksel kare olacak şekilde bir alan olarak oluşturulur. Oluşturucu, ilk görünümü vermek için çağırır DrawBitmap . angle Alan sıfırsa (yöntem ilk çağrıldığında olduğu gibi), bit eşlem X olarak çapraz iki çizgi içerir. Çizgiler, değerden bağımsız olarak angle bit eşlem kenarına her zaman ulaşacak kadar uzun yapılır:

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

Animasyon ek yükü ve OnDisappearing geçersiz kılmalarında OnAppearing gerçekleşir. yöntemi, OnTimerTick bit eşlem içinde X şeklini döndürmek için her 10 saniyede bir 0 dereceden 360 dereceye kadar değere animasyon angle uygular:

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

X rakamının simetrisi nedeniyle bu, değeri her 2,5 saniyede bir 0 dereceden 90 dereceye döndürmeyle angle aynıdır.

İşleyici PaintSurface bit eşlemden bir gölgelendirici oluşturur ve tuvalin tamamını renklendirmek için paint nesnesini kullanır:

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

Seçenekler, SKShaderTileMode.Mirror basit animasyonun önerdiğinden çok daha karmaşık görünen genel bir animasyon deseni oluşturmak için her bit eşlemdeki X'in kollarının bitişik bit eşlemlerde X ile birleştirilmesini sağlar:

Animasyonlu Bit Eşlem Kutucuğu