Condividi tramite


Creazione e disegno di bitmap SkiaSharp

Si è visto come un'applicazione può caricare bitmap dal Web, dalle risorse dell'applicazione e dalla raccolta foto dell'utente. È anche possibile creare nuove bitmap all'interno dell'applicazione. L'approccio più semplice prevede uno dei costruttori di SKBitmap:

SKBitmap bitmap = new SKBitmap(width, height);

I width parametri e height sono numeri interi e specificano le dimensioni pixel della bitmap. Questo costruttore crea una bitmap a colori intero con quattro byte per pixel: ognuno dei componenti rosso, verde, blu e alfa (opacità).

Dopo aver creato una nuova bitmap, è necessario ottenere qualcosa sulla superficie della bitmap. Questa operazione viene eseguita in genere in uno dei due modi seguenti:

  • Disegnare sulla bitmap usando metodi di disegno standard Canvas .
  • Accedere direttamente ai bit pixel.

Questo articolo illustra il primo approccio:

Esempio di disegno

Il secondo approccio è illustrato nell'articolo Accesso ai pixel bitmap SkiaSharp.

Disegno sulla bitmap

Il disegno sulla superficie di una bitmap è identico a quello di un disegno su una visualizzazione video. Per disegnare su una visualizzazione video, si ottiene un SKCanvas oggetto dagli argomenti dell'evento PaintSurface . Per disegnare su una bitmap, creare un SKCanvas oggetto usando il SKCanvas costruttore :

SKCanvas canvas = new SKCanvas(bitmap);

Al termine del disegno sulla bitmap, è possibile eliminare l'oggetto SKCanvas . Per questo motivo, il SKCanvas costruttore viene in genere chiamato in un'istruzione using :

using (SKCanvas canvas = new SKCanvas(bitmap))
{
    ··· // call drawing function
}

È quindi possibile visualizzare la bitmap. In un secondo momento, il programma può creare un nuovo SKCanvas oggetto basato su quella stessa bitmap e disegnare su di esso altri.

La pagina Hello Bitmap nell'applicazione di esempio scrive il testo "Hello, Bitmap!" in una bitmap e quindi visualizza tale bitmap più volte.

Il costruttore di HelloBitmapPage inizia creando un SKPaint oggetto per la visualizzazione del testo. Determina le dimensioni di una stringa di testo e crea una bitmap con tali dimensioni. Crea quindi un SKCanvas oggetto basato su tale bitmap, chiama Cleare quindi chiama DrawText. È sempre consigliabile chiamare Clear con una nuova bitmap perché una bitmap appena creata potrebbe contenere dati casuali.

Il costruttore termina creando un SKCanvasView oggetto per visualizzare la bitmap:

public partial class HelloBitmapPage : ContentPage
{
    const string TEXT = "Hello, Bitmap!";
    SKBitmap helloBitmap;

    public HelloBitmapPage()
    {
        Title = TEXT;

        // Create bitmap and draw on it
        using (SKPaint textPaint = new SKPaint { TextSize = 48 })
        {
            SKRect bounds = new SKRect();
            textPaint.MeasureText(TEXT, ref bounds);

            helloBitmap = new SKBitmap((int)bounds.Right,
                                       (int)bounds.Height);

            using (SKCanvas bitmapCanvas = new SKCanvas(helloBitmap))
            {
                bitmapCanvas.Clear();
                bitmapCanvas.DrawText(TEXT, 0, -bounds.Top, textPaint);
            }
        }

        // Create SKCanvasView to view result
        SKCanvasView canvasView = new SKCanvasView();
        canvasView.PaintSurface += OnCanvasViewPaintSurface;
        Content = canvasView;
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear(SKColors.Aqua);

        for (float y = 0; y < info.Height; y += helloBitmap.Height)
            for (float x = 0; x < info.Width; x += helloBitmap.Width)
            {
                canvas.DrawBitmap(helloBitmap, x, y);
            }
    }
}

Il PaintSurface gestore esegue il rendering della bitmap più volte in righe e colonne della visualizzazione. Si noti che il Clear metodo nel PaintSurface gestore ha un argomento , SKColors.Aquache colora lo sfondo della superficie di visualizzazione:

Ciao, Bitmap!

L'aspetto dello sfondo aqua rivela che la bitmap è trasparente ad eccezione del testo.

Cancellazione e trasparenza

La visualizzazione della pagina Hello Bitmap dimostra che la bitmap creata dal programma è trasparente, ad eccezione del testo nero. Ecco perché il colore azzurro della superficie di visualizzazione si mostra attraverso.

La documentazione dei Clear metodi di SKCanvas li descrive con l'istruzione : "Sostituisce tutti i pixel nella clip corrente dell'area di disegno". L'uso della parola "sostituisce" rivela una caratteristica importante di questi metodi: tutti i metodi di disegno di SKCanvas aggiungere qualcosa alla superficie di visualizzazione esistente. I Clear metodi sostituiscono ciò che è già presente.

Clear esiste in due versioni diverse:

  • Il Clear metodo con un SKColor parametro sostituisce i pixel della superficie di visualizzazione con i pixel di tale colore.

  • Il Clear metodo senza parametri sostituisce i pixel con il SKColors.Empty colore, ovvero un colore in cui tutti i componenti (rosso, verde, blu e alfa) sono impostati su zero. Questo colore viene talvolta definito "nero trasparente".

La chiamata Clear senza argomenti in una nuova bitmap inizializza l'intera bitmap in modo che sia completamente trasparente. Qualsiasi elemento successivamente disegnato sulla bitmap in genere sarà opaco o parzialmente opaco.

Ecco qualcosa da provare: nella pagina Hello Bitmap sostituire il Clear metodo applicato a bitmapCanvas con questo:

bitmapCanvas.Clear(new SKColor(255, 0, 0, 128));

L'ordine dei parametri del SKColor costruttore è rosso, verde, blu e alfa, in cui ogni valore può variare da 0 a 255. Tenere presente che un valore alfa pari a 0 è trasparente, mentre un valore alfa pari a 255 è opaco.

Il valore (255, 0, 0, 128) cancella i pixel bitmap in pixel rossi con opacità del 50%. Ciò significa che lo sfondo bitmap è semitrasparente. Lo sfondo rosso semitrasparente della bitmap si combina con lo sfondo aqua della superficie di visualizzazione per creare uno sfondo grigio.

Provare a impostare il colore del testo su nero trasparente inserendo l'assegnazione seguente nell'inizializzatore SKPaint :

Color = new SKColor(0, 0, 0, 0)

Si potrebbe pensare che questo testo trasparente creerebbe aree completamente trasparenti della bitmap attraverso cui si vedrà lo sfondo aqua della superficie di visualizzazione. Ma questo non è così. Il testo viene disegnato sopra ciò che è già presente nella bitmap. Il testo trasparente non sarà affatto visibile.

Nessun Draw metodo rende mai più trasparente una bitmap. Solo Clear in grado di farlo.

Tipi di colori bitmap

Il costruttore più semplice SKBitmap consente di specificare una larghezza e un'altezza di un numero intero di pixel per la bitmap. Altri SKBitmap costruttori sono più complessi. Questi costruttori richiedono argomenti di due tipi di enumerazione: SKColorType e SKAlphaType. Altri costruttori usano la SKImageInfo struttura , che consolida queste informazioni.

L'enumerazione SKColorType ha 9 membri. Ognuno di questi membri descrive un modo specifico per archiviare i pixel bitmap:

  • Unknown
  • Alpha8 — ogni pixel è a 8 bit, che rappresenta un valore alfa da completamente trasparente a completamente opaco
  • Rgb565 — ogni pixel è a 16 bit, 5 bit per rosso e blu e 6 per il verde
  • Argb4444 — ogni pixel è di 16 bit, 4 per alfa, rosso, verde e blu
  • Rgba8888 — ogni pixel è di 32 bit, 8 per rosso, verde, blu e alfa
  • Bgra8888 — ogni pixel è di 32 bit, 8 per blu, verde, rosso e alfa
  • Index8 — ogni pixel è a 8 bit e rappresenta un indice in un SKColorTable
  • Gray8 — ogni pixel è a 8 bit che rappresentano una sfumatura grigia dal nero al bianco
  • RgbaF16 — ogni pixel è a 64 bit, con rosso, verde, blu e alfa in un formato a virgola mobile a 16 bit

I due formati in cui ogni pixel è 32 pixel (4 byte) sono spesso chiamati formati a colori completi. Molti degli altri formati datano da un momento in cui i video visualizzano se stessi non erano in grado di colori completi. Le bitmap di colore limitato erano adeguate per questi schermi e consentivano alle bitmap di occupare meno spazio in memoria.

In questi giorni, i programmatori usano quasi sempre bitmap a colori completi e non preoccupano altri formati. L'eccezione è il RgbaF16 formato, che consente una risoluzione dei colori maggiore rispetto ai formati a colori completi. Tuttavia, questo formato viene usato per scopi specializzati, ad esempio l'imaging medico, e non ha molto senso quando usato con schermi standard a colori completi.

Questa serie di articoli si limiterà ai SKBitmap formati di colore utilizzati per impostazione predefinita quando non viene specificato alcun SKColorType membro. Questo formato predefinito è basato sulla piattaforma sottostante. Per le piattaforme supportate da Xamarin.Forms, il tipo di colore predefinito è:

  • Rgba8888 per iOS e Android
  • Bgra8888 per la piattaforma UWP

L'unica differenza è l'ordine dei 4 byte in memoria e questo diventa un problema solo quando si accede direttamente ai bit pixel. Questo non diventerà importante fino a quando non si arriva all'articolo Accesso ai pixel bitmap skiaSharp.

L'enumerazione SKAlphaType ha quattro membri:

  • Unknown
  • Opaque — la bitmap non ha trasparenza
  • Premul — i componenti di colore vengono pre-moltiplicati per il componente alfa
  • Unpremul — i componenti di colore non vengono pre-moltiplicati per il componente alfa

Ecco un pixel bitmap rosso a 4 byte con trasparenza del 50%, con i byte visualizzati nell'ordine rosso, verde, blu, alfa:

0xFF 0x00 0x00 0x80

Quando viene eseguito il rendering di una bitmap contenente pixel semitrasparti in una superficie di visualizzazione, i componenti di colore di ogni pixel bitmap devono essere moltiplicati per il valore alfa del pixel e i componenti di colore del pixel corrispondente della superficie di visualizzazione devono essere moltiplicati per 255 meno il valore alfa. I due pixel possono quindi essere combinati. Il rendering della bitmap può essere più veloce se i componenti di colore nei pixel bitmap sono già stati pre-mulitplied dal valore alfa. Lo stesso pixel rosso verrebbe archiviato come segue in un formato pre-moltiplicato:

0x80 0x00 0x00 0x80

Questo miglioramento delle prestazioni è il motivo per cui SkiaSharp le bitmap vengono create per impostazione predefinita con un Premul formato. Ma anche in questo caso, diventa necessario conoscerlo solo quando si accede e si modificano i bit di pixel.

Disegno su bitmap esistenti

Non è necessario creare una nuova bitmap per disegnare su di esso. È anche possibile disegnare su una bitmap esistente.

La pagina Monkey Moustache usa il relativo costruttore per caricare l'immagine MonkeyFace.png . Crea quindi un SKCanvas oggetto basato su tale bitmap e usa SKPaint oggetti e SKPath per disegnare un baffo su di esso:

public partial class MonkeyMoustachePage : ContentPage
{
    SKBitmap monkeyBitmap;

    public MonkeyMoustachePage()
    {
        Title = "Monkey Moustache";

        monkeyBitmap = BitmapExtensions.LoadBitmapResource(GetType(),
            "SkiaSharpFormsDemos.Media.MonkeyFace.png");

        // Create canvas based on bitmap
        using (SKCanvas canvas = new SKCanvas(monkeyBitmap))
        {
            using (SKPaint paint = new SKPaint())
            {
                paint.Style = SKPaintStyle.Stroke;
                paint.Color = SKColors.Black;
                paint.StrokeWidth = 24;
                paint.StrokeCap = SKStrokeCap.Round;

                using (SKPath path = new SKPath())
                {
                    path.MoveTo(380, 390);
                    path.CubicTo(560, 390, 560, 280, 500, 280);

                    path.MoveTo(320, 390);
                    path.CubicTo(140, 390, 140, 280, 200, 280);

                    canvas.DrawPath(path, paint);
                }
            }
        }

        // Create SKCanvasView to view result
        SKCanvasView canvasView = new SKCanvasView();
        canvasView.PaintSurface += OnCanvasViewPaintSurface;
        Content = canvasView;
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();
        canvas.DrawBitmap(monkeyBitmap, info.Rect, BitmapStretch.Uniform);
    }
}

Il costruttore termina creando un SKCanvasView oggetto il cui PaintSurface gestore visualizza semplicemente il risultato:

Moustache scimmia

Copia e modifica di bitmap

I metodi di SKCanvas che è possibile usare per disegnare su una bitmap includono DrawBitmap. Ciò significa che è possibile disegnare una bitmap su un'altra, in genere modificandola in qualche modo.

Il modo più versatile per modificare una bitmap consiste nell'accedere ai bit di pixel effettivi, un oggetto trattato nell'articolo Accesso ai pixel bitmap SkiaSharp. Esistono tuttavia molte altre tecniche per modificare le bitmap che non richiedono l'accesso ai bit pixel.

La bitmap seguente inclusa nell'applicazione di esempio è larga 360 pixel e 480 pixel in altezza:

Alpinisti

Si supponga di non aver ricevuto l'autorizzazione dalla scimmia a sinistra per pubblicare questa fotografia. Una soluzione consiste nell'oscurare il viso della scimmia usando una tecnica denominata pixelizzazione. I pixel del viso vengono sostituiti con blocchi di colore in modo da non rendere disponibili le caratteristiche. I blocchi di colore sono in genere derivati dall'immagine originale mediando i colori dei pixel corrispondenti a questi blocchi. Ma non è necessario eseguire questa media. Si verifica automaticamente quando si copia una bitmap in una dimensione pixel più piccola.

La faccia della scimmia sinistra occupa circa un'area quadrata di 72 pixel con un angolo superiore sinistro al punto (112, 238). Sostituiamo l'area quadrata di 72 pixel con una matrice di blocchi colorati 9 per 9, ognuno dei quali è quadrato di 8 per 8 pixel.

La pagina Pixelize Image viene caricata in tale bitmap e crea prima di tutto una piccola bitmap quadrata di 9 pixel denominata faceBitmap. Questa è una destinazione per copiare solo la faccia della scimmia. Il rettangolo di destinazione è solo un quadrato di 9 pixel, ma il rettangolo di origine è quadrato di 72 pixel. Ogni blocco di pixel di origine da 8 a 8 viene consolidato fino a un solo pixel mediando i colori.

Il passaggio successivo consiste nel copiare la bitmap originale in una nuova bitmap con le stesse dimensioni denominate pixelizedBitmap. Il minuscolo faceBitmap viene quindi copiato su quello con un rettangolo di destinazione quadrato a 72 pixel in modo che ogni pixel di faceBitmap venga espanso fino a 8 volte le dimensioni:

public class PixelizedImagePage : ContentPage
{
    SKBitmap pixelizedBitmap;

    public PixelizedImagePage ()
    {
        Title = "Pixelize Image";

        SKBitmap originalBitmap = BitmapExtensions.LoadBitmapResource(GetType(),
            "SkiaSharpFormsDemos.Media.MountainClimbers.jpg");

        // Create tiny bitmap for pixelized face
        SKBitmap faceBitmap = new SKBitmap(9, 9);

        // Copy subset of original bitmap to that
        using (SKCanvas canvas = new SKCanvas(faceBitmap))
        {
            canvas.Clear();
            canvas.DrawBitmap(originalBitmap,
                              new SKRect(112, 238, 184, 310),   // source
                              new SKRect(0, 0, 9, 9));          // destination

        }

        // Create full-sized bitmap for copy
        pixelizedBitmap = new SKBitmap(originalBitmap.Width, originalBitmap.Height);

        using (SKCanvas canvas = new SKCanvas(pixelizedBitmap))
        {
            canvas.Clear();

            // Draw original in full size
            canvas.DrawBitmap(originalBitmap, new SKPoint());

            // Draw tiny bitmap to cover face
            canvas.DrawBitmap(faceBitmap,
                              new SKRect(112, 238, 184, 310));  // destination
        }

        // Create SKCanvasView to view result
        SKCanvasView canvasView = new SKCanvasView();
        canvasView.PaintSurface += OnCanvasViewPaintSurface;
        Content = canvasView;
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();
        canvas.DrawBitmap(pixelizedBitmap, info.Rect, BitmapStretch.Uniform);
    }
}

Il costruttore termina creando un oggetto SKCanvasView per visualizzare il risultato:

Pixelize Image

Rotazione delle bitmap

Un'altra attività comune è la rotazione di bitmap. Ciò è particolarmente utile quando si recuperano bitmap da una libreria foto i Telefono o iPad. A meno che il dispositivo non sia stato mantenuto in un particolare orientamento quando è stata scattata la foto, è probabile che l'immagine sia capovolta o laterale.

L'impostazione di una bitmap a capovolto richiede la creazione di un'altra bitmap con le stesse dimensioni del primo e quindi l'impostazione di una trasformazione per ruotare di 180 gradi durante la copia del primo al secondo. In tutti gli esempi di questa sezione, bitmap è l'oggetto SKBitmap che è necessario ruotare:

SKBitmap rotatedBitmap = new SKBitmap(bitmap.Width, bitmap.Height);

using (SKCanvas canvas = new SKCanvas(rotatedBitmap))
{
    canvas.Clear();
    canvas.RotateDegrees(180, bitmap.Width / 2, bitmap.Height / 2);
    canvas.DrawBitmap(bitmap, new SKPoint());
}

Quando si ruota di 90 gradi, è necessario creare una bitmap con dimensioni diverse rispetto all'originale scambiando l'altezza e la larghezza. Ad esempio, se la bitmap originale è larga 1200 pixel e 800 pixel di altezza, la bitmap ruotata è larga 800 pixel e 1200 pixel di larghezza. Impostare la conversione e la rotazione in modo che la bitmap venga ruotata attorno all'angolo superiore sinistro e quindi spostata in visualizzazione. Tenere presente che i Translate metodi e RotateDegrees vengono chiamati nell'ordine opposto del modo in cui vengono applicati. Ecco il codice per la rotazione di 90 gradi in senso orario:

SKBitmap rotatedBitmap = new SKBitmap(bitmap.Height, bitmap.Width);

using (SKCanvas canvas = new SKCanvas(rotatedBitmap))
{
    canvas.Clear();
    canvas.Translate(bitmap.Height, 0);
    canvas.RotateDegrees(90);
    canvas.DrawBitmap(bitmap, new SKPoint());
}

Ed ecco una funzione simile per la rotazione di 90 gradi in senso antiorario:

SKBitmap rotatedBitmap = new SKBitmap(bitmap.Height, bitmap.Width);

using (SKCanvas canvas = new SKCanvas(rotatedBitmap))
{
    canvas.Clear();
    canvas.Translate(0, bitmap.Width);
    canvas.RotateDegrees(-90);
    canvas.DrawBitmap(bitmap, new SKPoint());
}

Questi due metodi vengono usati nelle pagine photo puzzle descritte nell'articolo Ritagliare le bitmap SkiaSharp.

Un programma che consente all'utente di ruotare una bitmap in incrementi di 90 gradi richiede l'implementazione di una sola funzione per la rotazione di 90 gradi. L'utente può quindi ruotare in qualsiasi incremento di 90 gradi eseguendo ripetutamente questa funzione.

Un programma può anche ruotare una bitmap in base a qualsiasi quantità. Un approccio semplice consiste nel modificare la funzione che ruota di 180 gradi sostituendo 180 con una variabile generalizzata angle :

SKBitmap rotatedBitmap = new SKBitmap(bitmap.Width, bitmap.Height);

using (SKCanvas canvas = new SKCanvas(rotatedBitmap))
{
    canvas.Clear();
    canvas.RotateDegrees(angle, bitmap.Width / 2, bitmap.Height / 2);
    canvas.DrawBitmap(bitmap, new SKPoint());
}

Tuttavia, nel caso generale, questa logica ritaglierà gli angoli della bitmap ruotata. Un approccio migliore consiste nel calcolare le dimensioni della bitmap ruotata usando i trigonometria per includere tali angoli.

Questo trigonometria viene visualizzato nella pagina Rotazione bitmap. Il file XAML crea un'istanza SKCanvasView di e un oggetto Slider che può variare da 0 a 360 gradi con un Label oggetto che mostra il valore corrente:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:skia="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
             x:Class="SkiaSharpFormsDemos.Bitmaps.BitmapRotatorPage"
             Title="Bitmap Rotator">
    <StackLayout>
        <skia:SKCanvasView x:Name="canvasView"
                           VerticalOptions="FillAndExpand"
                           PaintSurface="OnCanvasViewPaintSurface" />

        <Slider x:Name="slider"
                Maximum="360"
                Margin="10, 0"
                ValueChanged="OnSliderValueChanged" />

        <Label Text="{Binding Source={x:Reference slider},
                              Path=Value,
                              StringFormat='Rotate by {0:F0}&#x00B0;'}"
               HorizontalTextAlignment="Center" />

    </StackLayout>
</ContentPage>

Il file code-behind carica una risorsa bitmap e lo salva come campo di sola lettura statico denominato originalBitmap. La bitmap visualizzata nel PaintSurface gestore è rotatedBitmap, che inizialmente è impostata su originalBitmap:

public partial class BitmapRotatorPage : ContentPage
{
    static readonly SKBitmap originalBitmap =
        BitmapExtensions.LoadBitmapResource(typeof(BitmapRotatorPage),
            "SkiaSharpFormsDemos.Media.Banana.jpg");

    SKBitmap rotatedBitmap = originalBitmap;

    public BitmapRotatorPage ()
    {
        InitializeComponent ();
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();
        canvas.DrawBitmap(rotatedBitmap, info.Rect, BitmapStretch.Uniform);
    }

    void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
    {
        double angle = args.NewValue;
        double radians = Math.PI * angle / 180;
        float sine = (float)Math.Abs(Math.Sin(radians));
        float cosine = (float)Math.Abs(Math.Cos(radians));
        int originalWidth = originalBitmap.Width;
        int originalHeight = originalBitmap.Height;
        int rotatedWidth = (int)(cosine * originalWidth + sine * originalHeight);
        int rotatedHeight = (int)(cosine * originalHeight + sine * originalWidth);

        rotatedBitmap = new SKBitmap(rotatedWidth, rotatedHeight);

        using (SKCanvas canvas = new SKCanvas(rotatedBitmap))
        {
            canvas.Clear(SKColors.LightPink);
            canvas.Translate(rotatedWidth / 2, rotatedHeight / 2);
            canvas.RotateDegrees((float)angle);
            canvas.Translate(-originalWidth / 2, -originalHeight / 2);
            canvas.DrawBitmap(originalBitmap, new SKPoint());
        }

        canvasView.InvalidateSurface();
    }
}

Il ValueChanged gestore dell'oggetto Slider esegue le operazioni che creano un nuovo rotatedBitmap oggetto in base all'angolo di rotazione. La nuova larghezza e altezza si basano su valori assoluti di sines e coseni delle larghezze e altezze originali. Le trasformazioni utilizzate per disegnare la bitmap originale sulla bitmap ruotata spostano il centro bitmap originale nell'origine, quindi ruotarlo in base al numero specificato di gradi e quindi traslarlo al centro della bitmap ruotata. I Translate metodi e RotateDegrees vengono chiamati nell'ordine opposto rispetto al modo in cui vengono applicati.

Si noti l'uso del Clear metodo per rendere lo sfondo di rotatedBitmap un rosa chiaro. Si tratta esclusivamente di illustrare le dimensioni di rotatedBitmap sullo schermo:

Rotazione bitmap

La bitmap ruotata è sufficiente per includere l'intera bitmap originale, ma non più grande.

Capovolgimento di bitmap

Un'altra operazione comunemente eseguita sulle bitmap è detta capovolgimento. Concettualmente, la bitmap viene ruotata in tre dimensioni attorno a un asse verticale o all'asse orizzontale attraverso il centro della bitmap. Il capovolgimento verticale crea un'immagine speculare.

La pagina Bitmap Flipper nell'applicazione di esempio illustra questi processi. Il file XAML contiene un e SKCanvasView due pulsanti per capovolgere verticalmente e orizzontalmente:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:skia="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
             x:Class="SkiaSharpFormsDemos.Bitmaps.BitmapFlipperPage"
             Title="Bitmap Flipper">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

        <skia:SKCanvasView x:Name="canvasView"
                           Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"
                           PaintSurface="OnCanvasViewPaintSurface" />

        <Button Text="Flip Vertical"
                Grid.Row="1" Grid.Column="0"
                Margin="0, 10"
                Clicked="OnFlipVerticalClicked" />

        <Button Text="Flip Horizontal"
                Grid.Row="1" Grid.Column="1"
                Margin="0, 10"
                Clicked="OnFlipHorizontalClicked" />
    </Grid>
</ContentPage>

Il file code-behind implementa queste due operazioni nei Clicked gestori per i pulsanti:

public partial class BitmapFlipperPage : ContentPage
{
    SKBitmap bitmap =
        BitmapExtensions.LoadBitmapResource(typeof(BitmapRotatorPage),
            "SkiaSharpFormsDemos.Media.SeatedMonkey.jpg");

    public BitmapFlipperPage()
    {
        InitializeComponent();
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();
        canvas.DrawBitmap(bitmap, info.Rect, BitmapStretch.Uniform);
    }

    void OnFlipVerticalClicked(object sender, ValueChangedEventArgs args)
    {
        SKBitmap flippedBitmap = new SKBitmap(bitmap.Width, bitmap.Height);

        using (SKCanvas canvas = new SKCanvas(flippedBitmap))
        {
            canvas.Clear();
            canvas.Scale(-1, 1, bitmap.Width / 2, 0);
            canvas.DrawBitmap(bitmap, new SKPoint());
        }

        bitmap = flippedBitmap;
        canvasView.InvalidateSurface();
    }

    void OnFlipHorizontalClicked(object sender, ValueChangedEventArgs args)
    {
        SKBitmap flippedBitmap = new SKBitmap(bitmap.Width, bitmap.Height);

        using (SKCanvas canvas = new SKCanvas(flippedBitmap))
        {
            canvas.Clear();
            canvas.Scale(1, -1, 0, bitmap.Height / 2);
            canvas.DrawBitmap(bitmap, new SKPoint());
        }

        bitmap = flippedBitmap;
        canvasView.InvalidateSurface();
    }
}

Il capovolgimento verticale viene eseguito da una trasformazione di ridimensionamento con un fattore di ridimensionamento orizzontale pari a –1. Il centro di ridimensionamento è il centro verticale della bitmap. Il capovolgimento orizzontale è una trasformazione di ridimensionamento con un fattore di ridimensionamento verticale pari a -1.

Come si può vedere dalla lettera invertita sulla camicia della scimmia, capovolgimento non è uguale alla rotazione. Tuttavia, come dimostra lo screenshot UWP a destra, capovolgere sia orizzontalmente che verticalmente è uguale alla rotazione di 180 gradi:

Capovolgimento bitmap

Un'altra attività comune che può essere gestita usando tecniche simili consiste nel ritagliare una bitmap in un subset rettangolare. Questo articolo è descritto nell'articolo successivo ritagliare le bitmap SkiaSharp.