Visualizzazione segmentata delle bitmap SkiaSharp

Download Sample Scaricare l'esempio

L'oggetto SkiaSharp SKCanvas definisce un metodo denominato DrawBitmapNinePatch e due metodi denominati DrawBitmapLattice molto simili. Entrambi questi metodi eseguono il rendering di una bitmap alle dimensioni di un rettangolo di destinazione, ma invece di estendere la bitmap in modo uniforme, visualizzano parti della bitmap nelle dimensioni in pixel e estendono altre parti della bitmap in modo che si adatti al rettangolo:

Segmented Samples

Questi metodi vengono in genere usati per il rendering di bitmap che fanno parte di oggetti dell'interfaccia utente, ad esempio pulsanti. Durante la progettazione di un pulsante, in genere vuoi che le dimensioni di un pulsante siano basate sul contenuto del pulsante, ma probabilmente vuoi che il bordo del pulsante sia la stessa larghezza indipendentemente dal contenuto del pulsante. Si tratta di un'applicazione ideale di DrawBitmapNinePatch.

DrawBitmapNinePatch è un caso speciale, DrawBitmapLattice ma è più facile dei due metodi da usare e comprendere.

Visualizzazione a nove patch

Concettualmente, DrawBitmapNinePatch divide una bitmap in nove rettangoli:

Nine Patch

I rettangoli in corrispondenza dei quattro angoli vengono visualizzati nelle dimensioni dei pixel. Come indicano le frecce, le altre aree sui bordi della bitmap vengono estese orizzontalmente o verticalmente all'area del rettangolo di destinazione. Il rettangolo al centro è allungato sia orizzontalmente che verticalmente.

Se non c'è spazio sufficiente nel rettangolo di destinazione per visualizzare anche i quattro angoli nelle dimensioni dei pixel, vengono ridimensionati fino alle dimensioni disponibili e non vengono visualizzati i quattro angoli.

Per dividere una bitmap in questi nove rettangoli, è necessario specificare solo il rettangolo al centro. Questa è la sintassi del DrawBitmapNinePatch metodo :

canvas.DrawBitmapNinePatch(bitmap, centerRectangle, destRectangle, paint);

Il rettangolo centrale è relativo alla bitmap. Si tratta di un SKRectI valore (la versione integer di SKRect) e tutte le coordinate e le dimensioni sono in unità di pixel. Il rettangolo di destinazione è relativo alla superficie di visualizzazione. L'argomento paint è facoltativo.

La pagina Nine Patch Display nell'esempio SkiaSharpFormsDemos usa innanzitutto un costruttore statico per creare una proprietà statica pubblica di tipo SKBitmap:

public partial class NinePatchDisplayPage : ContentPage
{
    static NinePatchDisplayPage()
    {
        using (SKCanvas canvas = new SKCanvas(FiveByFiveBitmap))
        using (SKPaint paint = new SKPaint
        {
            Style = SKPaintStyle.Stroke,
            Color = SKColors.Red,
            StrokeWidth = 10
        })
        {
            for (int x = 50; x < 500; x += 100)
                for (int y = 50; y < 500; y += 100)
                {
                    canvas.DrawCircle(x, y, 40, paint);
                }
        }
    }

    public static SKBitmap FiveByFiveBitmap { get; } = new SKBitmap(500, 500);
    ···
}

Altre due pagine di questo articolo usano la stessa bitmap. La bitmap è quadrata di 500 pixel ed è costituita da una matrice di 25 cerchi, tutte le stesse dimensioni, ognuna che occupa un'area quadrata di 100 pixel:

Circle Grid

Il costruttore dell'istanza del programma crea un oggetto SKCanvasView con un PaintSurface gestore che usa DrawBitmapNinePatch per visualizzare la bitmap estesa all'intera superficie di visualizzazione:

public class NinePatchDisplayPage : ContentPage
{
    ···
    public NinePatchDisplayPage()
    {
        Title = "Nine-Patch Display";

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

        SKRectI centerRect = new SKRectI(100, 100, 400, 400);
        canvas.DrawBitmapNinePatch(FiveByFiveBitmap, centerRect, info.Rect);
    }
}

Il centerRect rettangolo comprende la matrice centrale di 16 cerchi. I cerchi negli angoli vengono visualizzati nelle dimensioni dei pixel e tutto il resto viene esteso di conseguenza:

Nine-Patch Display

La pagina UWP ha una larghezza di 500 pixel e quindi visualizza le righe superiore e inferiore come una serie di cerchi della stessa dimensione. In caso contrario, tutti i cerchi che non si trovano negli angoli vengono estesi per formare puntini di sospensione.

Per una strana visualizzazione di oggetti costituiti da una combinazione di cerchi e puntini di sospensione, provare a definire il rettangolo centrale in modo che si sovrapponga a righe e colonne di cerchi:

SKRectI centerRect = new SKRectI(150, 150, 350, 350);

Display reticolare

I due DrawBitmapLattice metodi sono simili a DrawBitmapNinePatch, ma sono generalizzati per qualsiasi numero di divisioni orizzontali o verticali. Queste divisioni sono definite da matrici di interi corrispondenti ai pixel.

Il DrawBitmapLattice metodo con parametri per queste matrici di numeri interi non sembra funzionare. Il DrawBitmapLattice metodo con un parametro di tipo SKLattice funziona ed è quello usato negli esempi illustrati di seguito.

La SKLattice struttura definisce quattro proprietà:

  • XDivs, una matrice di numeri interi
  • YDivs, una matrice di numeri interi
  • Flags, una matrice di , un tipo di SKLatticeFlagsenumerazione
  • Bounds di tipo Nullable<SKRectI> per specificare un rettangolo di origine facoltativo all'interno della bitmap

La XDivs matrice divide la larghezza della bitmap in strisce verticali. La prima striscia si estende da pixel 0 a sinistra a XDivs[0]. Viene eseguito il rendering di questa striscia nella larghezza del pixel. La seconda striscia si estende da XDivs[0] a XDivs[1]e viene allungata. La terza striscia si estende da XDivs[1] a XDivs[2] e viene eseguito il rendering nella larghezza dei pixel. L'ultima striscia si estende dall'ultimo elemento della matrice al bordo destro della bitmap. Se la matrice ha un numero pari di elementi, viene visualizzata nella larghezza dei pixel. In caso contrario, è allungato. Il numero totale di strip verticali è uno più del numero di elementi nella matrice.

La YDivs matrice è simile. Divide l'altezza della matrice in strisce orizzontali.

Insieme, la XDivs matrice e YDivs divide la bitmap in rettangoli. Il numero di rettangoli è uguale al prodotto del numero di strisce orizzontali e del numero di strisce verticali.

Secondo la documentazione di Skia, la Flags matrice contiene un elemento per ogni rettangolo, prima la riga superiore dei rettangoli, la seconda riga e così via. La Flags matrice è di tipo SKLatticeFlags, un'enumerazione con i membri seguenti:

  • Default con valore 0
  • Transparent con valore 1

Tuttavia, questi flag non sembrano funzionare come dovrebbero, ed è meglio ignorarli. Ma non impostare la Flags proprietà su null. Impostarlo su una matrice di SKLatticeFlags valori sufficientemente grandi da includere il numero totale di rettangoli.

La pagina Reticolare Nine Patch usa DrawBitmapLattice per simulare DrawBitmapNinePatch. Usa la stessa bitmap creata in NinePatchDisplayPage:

public class LatticeNinePatchPage : ContentPage
{
    SKBitmap bitmap = NinePatchDisplayPage.FiveByFiveBitmap;

    public LatticeNinePatchPage ()
    {
        Title = "Lattice Nine-Patch";

        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;

        SKLattice lattice = new SKLattice();
        lattice.XDivs = new int[] { 100, 400 };
        lattice.YDivs = new int[] { 100, 400 };
        lattice.Flags = new SKLatticeFlags[9]; 

        canvas.DrawBitmapLattice(bitmap, lattice, info.Rect);
    }
}

Entrambe le XDivs proprietà e YDivs sono impostate su matrici di soli due numeri interi, dividendo la bitmap in tre strisce sia orizzontalmente che verticalmente: da pixel 0 a pixel 100 (di cui viene eseguito il rendering nelle dimensioni del pixel), da pixel 100 a pixel 400 (esteso) e da pixel 400 a pixel 500 (dimensioni pixel). XDivs Insieme e YDivs definire un totale di 9 rettangoli, ovvero le dimensioni della Flags matrice. La semplice creazione della matrice è sufficiente per creare una matrice di SKLatticeFlags.Default valori.

Lo schermo è identico al programma precedente:

Lattice Nine-Patch

La pagina Visualizzazione reticolare divide la bitmap in 16 rettangoli:

public class LatticeDisplayPage : ContentPage
{
    SKBitmap bitmap = NinePatchDisplayPage.FiveByFiveBitmap;

    public LatticeDisplayPage()
    {
        Title = "Lattice Display";

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

        SKLattice lattice = new SKLattice();
        lattice.XDivs = new int[] { 100, 200, 400 };
        lattice.YDivs = new int[] { 100, 300, 400 };

        int count = (lattice.XDivs.Length + 1) * (lattice.YDivs.Length + 1);
        lattice.Flags = new SKLatticeFlags[count];

        canvas.DrawBitmapLattice(bitmap, lattice, info.Rect);
    }
}

Le XDivs matrici e YDivs sono leggermente diverse, causando la visualizzazione non abbastanza simmetrica come gli esempi precedenti:

Lattice Display

Nelle immagini iOS e Android a sinistra vengono visualizzati solo i cerchi più piccoli nelle dimensioni dei pixel. Tutto il resto è allungato.

La pagina Visualizzazione reticolare generalizza la creazione della Flags matrice, consentendo di sperimentare XDivs e YDivs più facilmente. In particolare, è necessario vedere cosa accade quando si imposta il primo elemento della XDivs matrice o YDivs su 0.