L'accesso ai bit di pixel bitmap SkiaSharpAccessing SkiaSharp bitmap pixel bits

Scaricare l'esempio scaricare l'esempioDownload Sample Download the sample

Come illustrato nell'articolo SkiaSharp salvataggio di bitmap per i file, generalmente le bitmap vengono archiviate nei file in un formato compresso, come JPEG o PNG.As you saw in the article Saving SkiaSharp bitmaps to files, bitmaps are generally stored in files in a compressed format, such as JPEG or PNG. Contrariamente, una bitmap di SkiaSharp archiviata nella memoria non viene compresso.In constrast, a SkiaSharp bitmap stored in memory is not compressed. Sono archiviati come una serie sequenziale di pixel.It is stored as a sequential series of pixels. Questo formato non compresso facilita il trasferimento delle bitmap a una superficie di visualizzazione.This uncompressed format facilitates the transfer of bitmaps to a display surface.

Il blocco di memoria occupato da una bitmap SkiaSharp è organizzato in modo molto semplice: Inizia con la prima riga di pixel, da sinistra a destra, quindi continua con la seconda riga.The memory block occupied by a SkiaSharp bitmap is organized in a very straightforward manner: It begins with the first row of pixels, from left to right, and then continues with the second row. Per le bitmap a colori, ogni pixel costituita da quattro byte, il che significa che lo spazio di memoria totale necessario per la bitmap è quattro volte il prodotto di larghezza e altezza.For full-color bitmaps, each pixel consists of four bytes, which means that the total memory space required by the bitmap is four times the product of its width and height.

Questo articolo descrive come un'applicazione può ottenere l'accesso a tali pixel, direttamente tramite l'accesso a blocco di memoria della bitmap in pixel, o indirettamente.This article describes how an application can get access to those pixels, either directly by accessing the bitmap's pixel memory block, or indirectly. In alcuni casi, potrebbe essere necessario un programma analizzare i pixel di un'immagine e creare un istogramma di qualche tipo.In some instances, a program might want to analyze the pixels of an image and construct a histogram of some sort. Più comunemente, le applicazioni possono costruire immagini univoche creando modo algoritmico i pixel che compongono la bitmap:More commonly, applications can construct unique images by algorithmically creating the pixels that make up the bitmap:

Esempi di bit di pixelPixel Bits Samples

Le tecnicheThe techniques

SkiaSharp offre diverse tecniche per l'accesso ai bit di pixel della bitmap.SkiaSharp provides several techniques for accessing a bitmap's pixel bits. Quello che si sceglie in genere è un compromesso tra la codifica di comodità (che è correlata alla manutenzione e la facilità di debug) e le prestazioni.Which one you choose is usually a compromise between coding convenience (which is related to maintenance and ease of debugging) and performance. Nella maggior parte dei casi, si userà uno dei seguenti metodi e proprietà di SKBitmap per l'accesso ai pixel della bitmap:In most cases, you'll use one of the following methods and properties of SKBitmap for accessing the bitmap's pixels:

  • Il GetPixel e SetPixel metodi consentono di ottenere o impostare il colore di un singolo pixel.The GetPixel and SetPixel methods allow you to obtain or set the color of a single pixel.
  • Il Pixels proprietà ottiene una matrice di colori dei pixel per l'intera bitmap, o imposta la matrice di colori.The Pixels property obtains an array of pixel colors for the entire bitmap, or sets the array of colors.
  • GetPixels Restituisce l'indirizzo della memoria pixel utilizzata per la bitmap.GetPixels returns the address of the pixel memory used by the bitmap.
  • SetPixels sostituisce l'indirizzo della memoria pixel utilizzata per la bitmap.SetPixels replaces the address of the pixel memory used by the bitmap.

È possibile considerare le prime due tecniche come "generale" e i due secondi come "livello basso."You can think of the first two techniques as "high level" and the second two as "low level." Esistono alcuni altri metodi e proprietà che è possibile usare, ma questi sono i più importanti.There are some other methods and properties that you can use, but these are the most valuable.

Consente di visualizzare le differenze di prestazioni tra queste tecniche, il SkiaSharpFormsDemos applicazione contiene una pagina denominata Bitmap sfumatura che Crea una bitmap con pixel che combinano le sfumature di colore rosso e blue per creare una sfumatura.To allow you to see the performance differences between these techniques, the SkiaSharpFormsDemos application contains a page named Gradient Bitmap that creates a bitmap with pixels that combine red and blue shades to create a gradient. Il programma consente di creare otto copie diverse di questa bitmap, tutti con tecniche diverse per l'impostazione di pixel della bitmap.The program creates eight different copies of this bitmap, all using different techniques for setting the bitmap pixels. Ognuno di tali otto bitmap viene creato in un metodo separato che imposta una breve descrizione della tecnica e calcola il tempo necessario per impostare tutti i pixel.Each of these eight bitmaps is created in a separate method that also sets a brief text description of the technique and calculates the time required to set all the pixels. Ogni metodo esegue il ciclo attraverso la logica di impostazione di pixel 100 volte per ottenere una stima più accurata delle prestazioni.Each method loops through the pixel-setting logic 100 times to get a better estimate of the performance.

Il metodo SetPixelThe SetPixel method

Se è sufficiente impostare o ottenere singoli pixel diverse, il SetPixel e GetPixel metodi sono ideali.If you only need to set or get several individual pixels, the SetPixel and GetPixel methods are ideal. Per ognuno di questi due metodi, specificare la colonna di tipo integer e riga.For each of these two methods, you specify the integer column and row. Indipendentemente dal formato pixel, questi due metodi consentono di ottenere o impostare il numero di pixel come un SKColor valore:Regardless of the pixel format, these two methods let you obtain or set the pixel as an SKColor value:

bitmap.SetPixel(col, row, color);

SKColor color = bitmap.GetPixel(col, row);

Il col argomento deve essere compresa tra 0 e uno minore del Width proprietà bitmap e row compreso tra 0 e uno minore del Height proprietà.The col argument must range from 0 to one less than the Width property of the bitmap, and row ranges from 0 to one less than the Height property.

Ecco il metodo Bitmap sfumatura che imposta il contenuto di un bitmap utilizzando il SetPixel (metodo).Here's the method in Gradient Bitmap that sets the contents of a bitmap using the SetPixel method. La bitmap è 256 x 256 pixel e il for i cicli sono impostate come hardcoded con l'intervallo di valori:The bitmap is 256 by 256 pixels, and the for loops are hard-coded with the range of values:

public class GradientBitmapPage : ContentPage
{
    const int REPS = 100;

    Stopwatch stopwatch = new Stopwatch();
    ···
    SKBitmap FillBitmapSetPixel(out string description, out int milliseconds)
    {
        description = "SetPixel";
        SKBitmap bitmap = new SKBitmap(256, 256);

        stopwatch.Restart();

        for (int rep = 0; rep < REPS; rep++)
            for (int row = 0; row < 256; row++)
                for (int col = 0; col < 256; col++)
                {
                    bitmap.SetPixel(col, row, new SKColor((byte)col, 0, (byte)row));
                }

        milliseconds = (int)stopwatch.ElapsedMilliseconds;
        return bitmap;
    }
    ···
}

Il set di colori per ogni pixel ha un componente rossa uguale alla colonna di bitmap e un componente blu uguale alla riga.The color set for each pixel has a red component equal to the bitmap column, and a blue component equal to the row. La bitmap risultante è nera in alto a sinistra, rosso in alto a destra, blu in basso a sinistra e magenta in basso a destra, con sfumature altrove.The resultant bitmap is black at the upper-left, red at the upper-right, blue at the lower-left, and magenta at the lower-right, with gradients elsewhere.

Il SetPixel metodo è chiamato 65.536 volte e indipendentemente dal fatto che come efficiente questo metodo potrebbe essere, in genere non è una buona idea eseguire che molte chiamate di API se è disponibile un'alternativa.The SetPixel method is called 65,536 times, and regardless how efficient this method might be, it's generally not a good idea to make that many API calls if an alternative is available. Per fortuna, sono disponibili numerose alternative.Fortunately, there are several alternatives.

La proprietà di pixelThe Pixels property

SKBitmap definisce un Pixels proprietà che restituisce una matrice di SKColor i valori per l'intera bitmap.SKBitmap defines a Pixels property that returns an array of SKColor values for the entire bitmap. È anche possibile usare Pixels per impostare una matrice di valori di colore per la bitmap:You can also use Pixels to set an array of color values for the bitmap:

SKColor[] pixels = bitmap.Pixels;

bitmap.Pixels = pixels;

I pixel vengono disposti nella matrice a partire dalla prima riga, da sinistra a destra, quindi la seconda riga e così via.The pixels are arranged in the array starting with the first row, from left to right, then the second row, and so forth. Il numero totale di colori nella matrice è uguale al prodotto di bitmap larghezza e altezza.The total number of colors in the array is equal to the product of the bitmap width and height.

Anche se questa proprietà viene visualizzata sia efficiente, tenere presente che i pixel vengono copiati dalla bitmap nella matrice e dalla matrice in bitmap e i pixel vengono convertiti da e verso SKColor valori.Although this property appears to be efficient, keep in mind that the pixels are being copied from the bitmap into the array, and from the array back into the bitmap, and the pixels are converted from and to SKColor values.

Ecco il metodo GradientBitmapPage classe che consente di impostare la mappa di bit usando la Pixels proprietà.Here's the method in the GradientBitmapPage class that sets the bitmap using the Pixels property. Il metodo alloca un' SKColor matrice di dimensioni necessarie, ma sarebbe possibile utilizzare il Pixels proprietà per creare tale matrice:The method allocates an SKColor array of the required size, but it could have used the Pixels property to create that array:

SKBitmap FillBitmapPixelsProp(out string description, out int milliseconds)
{
    description = "Pixels property";
    SKBitmap bitmap = new SKBitmap(256, 256);

    stopwatch.Restart();

    SKColor[] pixels = new SKColor[256 * 256];

    for (int rep = 0; rep < REPS; rep++)
        for (int row = 0; row < 256; row++)
            for (int col = 0; col < 256; col++)
            {
                pixels[256 * row + col] = new SKColor((byte)col, 0, (byte)row);
            }

    bitmap.Pixels = pixels;

    milliseconds = (int)stopwatch.ElapsedMilliseconds;
    return bitmap;
}

Si noti che l'indice della pixels deve essere calcolato dalla matrice di row e col variabili.Notice that the index of the pixels array needs to be calculated from the row and col variables. La riga viene moltiplicata per il numero di pixel in ogni riga (256 in questo caso) e quindi la colonna viene aggiunta.The row is multiplied by the number of pixels in each row (256 in this case), and then the column is added.

SKBitmap definisce inoltre una simile Bytes proprietà, che restituisce una matrice di byte per l'intera bitmap, ma è più complessa per le bitmap a colori.SKBitmap also defines a similar Bytes property, which returns a byte array for the entire bitmap, but it is more cumbersome for full-color bitmaps.

Il puntatore GetPixelsThe GetPixels pointer

Potenzialmente è la tecnica più potente per accedere ai pixel della bitmap GetPixels , non deve essere confusa con il GetPixel metodo o il Pixels proprietà.Potentially the most powerful technique to access the bitmap pixels is GetPixels, not to be confused with the GetPixel method or the Pixels property. Si noterà immediatamente una differenza con GetPixels in quanto restituisce un elemento non è molto comune nella programmazione c#:You'll immediately notice a difference with GetPixels in that it returns something not very common in C# programming:

IntPtr pixelsAddr = bitmap.GetPixels();

.NET IntPtr tipo rappresenta un puntatore.The .NET IntPtr type represents a pointer. Viene chiamato IntPtr perché è la lunghezza di un intero nel processore nativo del computer in cui il programma viene eseguito, in genere a 32 bit o 64 bit di lunghezza.It is called IntPtr because it is the length of an integer on the native processor of the machine on which the program is run, generally either 32 bits or 64 bits in length. Il IntPtr che GetPixels restituisce è l'indirizzo del blocco di memoria usata per archiviare i pixel dell'oggetto bitmap effettivo.The IntPtr that GetPixels returns is the address of the actual block of memory that the bitmap object is using to store its pixels.

È possibile convertire le IntPtr in un linguaggio c# puntatore tipo usando il ToPointer (metodo).You can convert the IntPtr into a C# pointer type using the ToPointer method. La sintassi di puntatore in c# è lo stesso come C e C++:The C# pointer syntax is the same as C and C++:

byte* ptr = (byte*)pixelsAddr.ToPointer();

Il ptr variabile è di tipo puntatore ai byte.The ptr variable is of type byte pointer. Ciò ptr variabile consente di accedere ai singoli byte di memoria che vengono usati per archiviare i pixel della bitmap.This ptr variable allows you to access the individual bytes of memory that are used to store the bitmap's pixels. Per leggere un byte la memoria o scrivere un byte per la memoria è usare codice simile al seguente:You use code like this to read a byte from this memory or write a byte to the memory:

byte pixelComponent = *ptr;

*ptr = pixelComponent;

In questo contesto, l'asterisco è il linguaggio c# operatore di riferimento indiretto e viene usato per fare riferimento al contenuto della memoria a cui punta ptr.In this context, the asterisk is the C# indirection operator and is used to reference the contents of the memory pointed to by ptr. Inizialmente ptr punta al primo byte del pixel prima della prima riga della bitmap, ma è possibile eseguire operazioni aritmetiche sul ptr variabile per spostarlo in altre posizioni all'interno della bitmap.Initially, ptr points to the first byte of the first pixel of the first row of the bitmap, but you can perform arithmetic on the ptr variable to move it to other locations within the bitmap.

Uno svantaggio è che è possibile usare ciò ptr variabile solo in un blocco di codice contrassegnato con il unsafe (parola chiave).One drawback is that you can use this ptr variable only in a code block marked with the unsafe keyword. Inoltre, l'assembly deve essere contrassegnato in modo che blocchi unsafe.In addition, the assembly must be flagged as allowing unsafe blocks. Questa operazione viene eseguita nelle proprietà del progetto.This is done in the project's properties.

Tramite i puntatori in c# è molto potente, ma anche molto pericolose.Using pointers in C# is very powerful, but also very dangerous. È necessario assicurarsi che sia non accedere memoria oltre ciò che dovrebbe per fare riferimento il puntatore del mouse.You need to be careful that you don't access memory beyond what the pointer is supposed to reference. Si tratta di uno dei motivi per usare puntatore associato con la parola "unsafe".This is why pointer use is associated with the word "unsafe."

Ecco il metodo GradientBitmapPage classe che utilizza il GetPixels (metodo).Here's the method in the GradientBitmapPage class that uses the GetPixels method. Si noti che il unsafe blocco che comprende tutto il codice usando il puntatore ai byte:Notice the unsafe block that encompasses all the code using the byte pointer:

SKBitmap FillBitmapBytePtr(out string description, out int milliseconds)
{
    description = "GetPixels byte ptr";
    SKBitmap bitmap = new SKBitmap(256, 256);

    stopwatch.Restart();

    IntPtr pixelsAddr = bitmap.GetPixels();

    unsafe
    {
        for (int rep = 0; rep < REPS; rep++)
        {
            byte* ptr = (byte*)pixelsAddr.ToPointer();

            for (int row = 0; row < 256; row++)
                for (int col = 0; col < 256; col++)
                {
                    *ptr++ = (byte)(col);   // red
                    *ptr++ = 0;             // green
                    *ptr++ = (byte)(row);   // blue
                    *ptr++ = 0xFF;          // alpha
                }
        }
    }

    milliseconds = (int)stopwatch.ElapsedMilliseconds;
    return bitmap;
}

Quando la ptr variabile prima di tutto viene ottenuta dal ToPointer (metodo), punta al primo byte del pixel più a sinistra della prima riga della bitmap.When the ptr variable is first obtained from the ToPointer method, it points to the first byte of the leftmost pixel of the first row of the bitmap. Il for loop per row e col sono impostati in modo che ptr può essere incrementato con la ++ operatore dopo ogni byte di ogni pixel è impostato.The for loops for row and col are set up so that ptr can be incremented with the ++ operator after each byte of each pixel is set. Per le altre 99 scorre in ciclo i pixel, di ptr deve essere impostata torna all'inizio della bitmap.For the other 99 loops through the pixels, the ptr must be set back to the beginning of the bitmap.

Ogni pixel è quattro byte di memoria, pertanto è necessario impostare separatamente ogni byte.Each pixel is four bytes of memory, so each byte has to be set separately. Qui il codice si presuppone che i byte sono in rosso ordine, verde, blu e alfa, che è coerente con il SKColorType.Rgba8888 tipo dei colori.The code here assumes that the bytes are in the order red, green, blue, and alpha, which is consistent with the SKColorType.Rgba8888 color type. Si ricorderà che questo è il tipo di colore predefinito per iOS e Android, ma non per la piattaforma Windows universale.You might recall that this is the default color type for iOS and Android, but not for the Universal Windows Platform. Per impostazione predefinita, la piattaforma UWP crea le bitmap con il SKColorType.Bgra8888 tipo dei colori.By default, the UWP creates bitmaps with the SKColorType.Bgra8888 color type. Per questo motivo, prevede di visualizzare alcuni risultati diversi che utilizzano tale piattaforma.For this reason, expect to see some different results on that platform!

È possibile eseguire il cast del valore restituito da ToPointer a un uint puntatore anziché un byte puntatore.It's possible to cast the value returned from ToPointer to a uint pointer rather than a byte pointer. In questo modo un pixel intero accessibile in un'unica istruzione.This allows an entire pixel to be accessed in one statement. Applicando la ++ operatore da tale puntatore aumenta in modo da quattro byte in modo che punti al pixel Avanti:Applying the ++ operator to that pointer increments it by four bytes to point to the next pixel:

public class GradientBitmapPage : ContentPage
{
    ···
    SKBitmap FillBitmapUintPtr(out string description, out int milliseconds)
    {
        description = "GetPixels uint ptr";
        SKBitmap bitmap = new SKBitmap(256, 256);

        stopwatch.Restart();

        IntPtr pixelsAddr = bitmap.GetPixels();

        unsafe
        {
            for (int rep = 0; rep < REPS; rep++)
            {
                uint* ptr = (uint*)pixelsAddr.ToPointer();

                for (int row = 0; row < 256; row++)
                    for (int col = 0; col < 256; col++)
                    {
                        *ptr++ = MakePixel((byte)col, 0, (byte)row, 0xFF);
                    }
            }
        }

        milliseconds = (int)stopwatch.ElapsedMilliseconds;
        return bitmap;
    }
    ···
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    uint MakePixel(byte red, byte green, byte blue, byte alpha) =>
            (uint)((alpha << 24) | (blue << 16) | (green << 8) | red);
    ···
}

Il pixel è impostato tramite la MakePixel metodo che costruisce un pixel intero dai componenti rossi, verdi, blu e alfa.The pixel is set using the MakePixel method, which constructs an integer pixel from red, green, blue, and alpha components. Tenere presente che il SKColorType.Rgba8888 formato ha un byte pixel ordinamento simile al seguente:Keep in mind that the SKColorType.Rgba8888 format has a pixel byte ordering like this:

RR GG, BB AARR GG BB AA

Ma il numero intero che corrisponde a tali byte è:But the integer corresponding to those bytes is:

AABBGGRRAABBGGRR

Il byte meno significativo dell'intero archiviato per primo in conformità con architettura little-endian.The least significant byte of the integer is stored first in accordance with little-endian architecture. Ciò MakePixel metodo non funziona correttamente per le bitmap con il Bgra8888 tipo dei colori.This MakePixel method will not work correctly for bitmaps with the Bgra8888 color type.

Il MakePixel metodo è contrassegnato con il MethodImplOptions.AggressiveInlining opzione a incoraggiare al compilatore di evitare rendendo questo un metodo separato, ma per compilare il codice in cui viene chiamato il metodo.The MakePixel method is flagged with the MethodImplOptions.AggressiveInlining option to encourage the compiler to avoid making this a separate method, but instead to compile the code where the method is called. Questa vista migliora le prestazioni.This should improve performance.

È interessante notare che il SKColor struttura definisce una conversione esplicita dal SKColor in un intero senza segno, il che significa che un SKColor valore può essere creato e una conversione uint può essere usato al posto di MakePixel:Interestingly, the SKColor structure defines an explicit conversion from SKColor to an unsigned integer, which means that an SKColor value can be created, and a conversion to uint can be used instead of MakePixel:

SKBitmap FillBitmapUintPtrColor(out string description, out int milliseconds)
{
    description = "GetPixels SKColor";
    SKBitmap bitmap = new SKBitmap(256, 256);

    stopwatch.Restart();

    IntPtr pixelsAddr = bitmap.GetPixels();

    unsafe
    {
        for (int rep = 0; rep < REPS; rep++)
        {
            uint* ptr = (uint*)pixelsAddr.ToPointer();

            for (int row = 0; row < 256; row++)
                for (int col = 0; col < 256; col++)
                {
                    *ptr++ = (uint)new SKColor((byte)col, 0, (byte)row);
                }
        }
    }

    milliseconds = (int)stopwatch.ElapsedMilliseconds;
    return bitmap;
}

L'unica domanda è la seguente: È il formato intero del SKColor valore nell'ordine SKColorType.Rgba8888 del tipo di colore o del SKColorType.Bgra8888 tipo di colore oppure è interamente un altro?The only question is this: Is the integer format of the SKColor value in the order of the SKColorType.Rgba8888 color type, or the SKColorType.Bgra8888 color type, or is it something else entirely? La risposta alla domanda dovrà essere rivelata al più presto.The answer to that question shall be revealed shortly.

Metodo SetPixelsThe SetPixels Method

SKBitmap definisce anche un metodo denominato SetPixels , che viene chiamato come segue:SKBitmap also defines a method named SetPixels, which you call like this:

bitmap.SetPixels(intPtr);

È importante ricordare che GetPixels Ottiene un IntPtr che fa riferimento il blocco di memoria utilizzato da bitmap per archiviare i pixel.Recall that GetPixels obtains an IntPtr referencing the memory block used by the bitmap to store its pixels. Il SetPixels chiamare sostituisce tale blocco di memoria con il blocco di memoria a cui fanno riferimento le IntPtr specificato come il SetPixels argomento.The SetPixels call replaces that memory block with the memory block referenced by the IntPtr specified as the SetPixels argument. La bitmap di quindi libera il blocco di memoria che utilizzava in precedenza.The bitmap then frees up the memory block it was using previously. La volta successiva GetPixels viene chiamato, ottiene il blocco di memoria impostato con SetPixels.The next time GetPixels is called, it obtains the memory block set with SetPixels.

Inizialmente, può sembrare come se SetPixels ti offre non sono più potenza e prestazioni rispetto a GetPixels pur essendo meno utile.At first, it seems as if SetPixels gives you no more power and performance than GetPixels while being less convenient. Con GetPixels è ottenere il blocco di memoria della bitmap e accedervi.With GetPixels you obtain the bitmap memory block and access it. Con SetPixels è possibile allocare e accedere a parte della memoria e quindi impostare questo valore come il blocco di memoria della bitmap.With SetPixels you allocate and access some memory, and then set that as the bitmap memory block.

Tuttavia, SetPixels l'utilizzo di offre un vantaggio sintattico distinto: Consente di accedere ai bit di pixel bitmap usando una matrice.But using SetPixels offers a distinct syntactic advantage: It allows you to access the bitmap pixel bits using an array. Ecco il metodo GradientBitmapPage che illustra questa tecnica.Here's the method in GradientBitmapPage that demonstrates this technique. Prima di tutto, il metodo definisce una matrice di byte multidimensionale corrispondenti ai byte di pixel della bitmap.The method first defines a multi-dimensional byte array corresponding to the bytes of the bitmap's pixels. La prima dimensione è la riga, la seconda dimensione è la colonna e corrisponde la terza dimensione a quattro componenti di ogni pixel:The first dimension is the row, the second dimension is the column, and the third dimension corresonds to the four components of each pixel:

SKBitmap FillBitmapByteBuffer(out string description, out int milliseconds)
{
    description = "SetPixels byte buffer";
    SKBitmap bitmap = new SKBitmap(256, 256);

    stopwatch.Restart();

    byte[,,] buffer = new byte[256, 256, 4];

    for (int rep = 0; rep < REPS; rep++)
        for (int row = 0; row < 256; row++)
            for (int col = 0; col < 256; col++)
            {
                buffer[row, col, 0] = (byte)col;   // red
                buffer[row, col, 1] = 0;           // green
                buffer[row, col, 2] = (byte)row;   // blue
                buffer[row, col, 3] = 0xFF;        // alpha
            }

    unsafe
    {
        fixed (byte* ptr = buffer)
        {
            bitmap.SetPixels((IntPtr)ptr);
        }
    }

    milliseconds = (int)stopwatch.ElapsedMilliseconds;
    return bitmap;
}

Quindi, dopo che la matrice è stata compilata con pixel, un unsafe blocco e una fixed istruzione consente di ottenere un puntatore ai byte che fa riferimento a questa matrice.Then, after the array has been filled with pixels, an unsafe block and a fixed statement is used to obtain a byte pointer that points to this array. Tale puntatore ai byte può quindi essere convertito in un IntPtr da passare al SetPixels.That byte pointer can then be cast to an IntPtr to pass to SetPixels.

La matrice creata non deve essere una matrice di byte.The array that you create doesn't have to be a byte array. Può essere una matrice di interi con solo due dimensioni per la colonna e riga:It can be an integer array with only two dimensions for the row and column:

SKBitmap FillBitmapUintBuffer(out string description, out int milliseconds)
{
    description = "SetPixels uint buffer";
    SKBitmap bitmap = new SKBitmap(256, 256);

    stopwatch.Restart();

    uint[,] buffer = new uint[256, 256];

    for (int rep = 0; rep < REPS; rep++)
        for (int row = 0; row < 256; row++)
            for (int col = 0; col < 256; col++)
            {
                buffer[row, col] = MakePixel((byte)col, 0, (byte)row, 0xFF);
            }

    unsafe
    {
        fixed (uint* ptr = buffer)
        {
            bitmap.SetPixels((IntPtr)ptr);
        }
    }

    milliseconds = (int)stopwatch.ElapsedMilliseconds;
    return bitmap;
}

Il MakePixel metodo anche in questo caso viene usato per combinare i componenti di colore in un pixel a 32 bit.The MakePixel method is again used to combine the color components into a 32-bit pixel.

Solo per motivi di completezza, ecco lo stesso codice, ma con un SKColor cast a un intero senza segno:Just for completeness, here's the same code but with an SKColor value cast to an unsigned integer:

SKBitmap FillBitmapUintBufferColor(out string description, out int milliseconds)
{
    description = "SetPixels SKColor";
    SKBitmap bitmap = new SKBitmap(256, 256);

    stopwatch.Restart();

    uint[,] buffer = new uint[256, 256];

    for (int rep = 0; rep < REPS; rep++)
        for (int row = 0; row < 256; row++)
            for (int col = 0; col < 256; col++)
            {
                buffer[row, col] = (uint)new SKColor((byte)col, 0, (byte)row);
            }

    unsafe
    {
        fixed (uint* ptr = buffer)
        {
            bitmap.SetPixels((IntPtr)ptr);
        }
    }

    milliseconds = (int)stopwatch.ElapsedMilliseconds;
    return bitmap;
}

Confronto tra le tecnicheComparing the techniques

Il costruttore del colore sfumato pagina chiama tutte le otto versioni dei metodi illustrati in precedenza e Salva i risultati:The constructor of the Gradient Color page calls all eight of the methods shown above, and saves the results:

public class GradientBitmapPage : ContentPage
{
    ···
    string[] descriptions = new string[8];
    SKBitmap[] bitmaps = new SKBitmap[8];
    int[] elapsedTimes = new int[8];

    SKCanvasView canvasView;

    public GradientBitmapPage ()
    {
        Title = "Gradient Bitmap";

        bitmaps[0] = FillBitmapSetPixel(out descriptions[0], out elapsedTimes[0]);
        bitmaps[1] = FillBitmapPixelsProp(out descriptions[1], out elapsedTimes[1]);
        bitmaps[2] = FillBitmapBytePtr(out descriptions[2], out elapsedTimes[2]);
        bitmaps[4] = FillBitmapUintPtr(out descriptions[4], out elapsedTimes[4]);
        bitmaps[6] = FillBitmapUintPtrColor(out descriptions[6], out elapsedTimes[6]);
        bitmaps[3] = FillBitmapByteBuffer(out descriptions[3], out elapsedTimes[3]);
        bitmaps[5] = FillBitmapUintBuffer(out descriptions[5], out elapsedTimes[5]);
        bitmaps[7] = FillBitmapUintBufferColor(out descriptions[7], out elapsedTimes[7]);

        canvasView = new SKCanvasView();
        canvasView.PaintSurface += OnCanvasViewPaintSurface;
        Content = canvasView;
    }
    ···
}

Il costruttore si conclude con la creazione di un SKCanvasView per visualizzare le bitmap risultante.The constructor concludes by creating an SKCanvasView to display the resultant bitmaps. Il PaintSurface gestore divide la superficie in otto rettangoli e le chiamate Display visualizzare ciascuna di esse:The PaintSurface handler divides its surface into eight rectangles and calls Display to display each one:

public class GradientBitmapPage : ContentPage
{
    ···
    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        int width = info.Width;
        int height = info.Height;

        canvas.Clear();

        Display(canvas, 0, new SKRect(0, 0, width / 2, height / 4));
        Display(canvas, 1, new SKRect(width / 2, 0, width, height / 4));
        Display(canvas, 2, new SKRect(0, height / 4, width / 2, 2 * height / 4));
        Display(canvas, 3, new SKRect(width / 2, height / 4, width, 2 * height / 4));
        Display(canvas, 4, new SKRect(0, 2 * height / 4, width / 2, 3 * height / 4));
        Display(canvas, 5, new SKRect(width / 2, 2 * height / 4, width, 3 * height / 4));
        Display(canvas, 6, new SKRect(0, 3 * height / 4, width / 2, height));
        Display(canvas, 7, new SKRect(width / 2, 3 * height / 4, width, height));
    }

    void Display(SKCanvas canvas, int index, SKRect rect)
    {
        string text = String.Format("{0}: {1:F1} msec", descriptions[index],
                                    (double)elapsedTimes[index] / REPS);

        SKRect bounds = new SKRect();

        using (SKPaint textPaint = new SKPaint())
        {
            textPaint.TextSize = (float)(12 * canvasView.CanvasSize.Width / canvasView.Width);
            textPaint.TextAlign = SKTextAlign.Center;
            textPaint.MeasureText("Tly", ref bounds);

            canvas.DrawText(text, new SKPoint(rect.MidX, rect.Bottom - bounds.Bottom), textPaint);
            rect.Bottom -= bounds.Height;
            canvas.DrawBitmap(bitmaps[index], rect, BitmapStretch.Uniform);
        }
    }
}

Per consentire al compilatore di ottimizzare il codice, questa pagina è stata eseguita in rilascio modalità.To allow the compiler to optimize the code, this page was run in Release mode. Ecco la pagina in esecuzione in un simulatore di iPhone 8 in un MacBook Pro, un telefono Android di Nexus 5 e Surface Pro 3 che esegue Windows 10.Here's that page running on an iPhone 8 simulator on a MacBook Pro, a Nexus 5 Android phone, and Surface Pro 3 running Windows 10. A causa delle differenze di hardware, evitare di confronto tra i tempi di prestazioni tra i dispositivi, ma invece consultare i tempi relativi in ogni dispositivo:Because of the hardware differences, avoid comparing the performance times between the devices, but instead look at the relative times on each device:

Bitmap sfumaturaGradient Bitmap

Ecco una tabella che consolida i tempi di esecuzione in millisecondi:Here is a table that consolidates the execution times in milliseconds:

APIAPI Tipo di datiData type iOSiOS AndroidAndroid UWPUWP
SetPixelSetPixel 3.173.17 10.7710.77 versione 3.493.49
PixelPixels per 0,320.32 1,231.23 0,070.07
GetPixelsGetPixels bytebyte 0,090.09 0,240.24 0.100.10
uintuint 0,060.06 0,260.26 0,050.05
SKColorSKColor 0,290.29 0,990.99 0,070.07
SetPixelsSetPixels bytebyte 1.331.33 6.786.78 0.110.11
uintuint 0,140.14 0.690.69 0,060.06
SKColorSKColor 0.350.35 1.931.93 0.100.10

Come previsto, la chiamata SetPixel volte 65.536 è il modo effeicient minimi per impostare i pixel della bitmap.As expected, calling SetPixel 65,536 times is the least effeicient way to set a bitmap's pixels. La compilazione un' SKColor matrice e impostando il Pixels proprietà è molto meglio e persino Confronta agevolmente con alcune del GetPixels e SetPixels tecniche.Filling an SKColor array and setting the Pixels property is much better, and even compares favorably with some of the GetPixels and SetPixels techniques. Lavora uint valori di pixel è veloce rispetto all'impostazione separato byte componenti e la conversione il SKColor valore intero senza segno aggiunge overhead per il processo.Working with uint pixel values is generally faster than setting separate byte components, and converting the SKColor value to an unsigned integer adds some overhead to the process.

È anche interessante confrontare le varie sfumature: Le prime righe di ogni piattaforma sono le stesse e mostrano la sfumatura come previsto.It's also interesting to compare the various gradients: The top rows of each platform are the same, and show the gradient as it was intended. Ciò significa che il SetPixel metodo e Pixels proprietà creare correttamente pixel da colori indipendentemente dal formato pixel sottostante.This means that the SetPixel method and the Pixels property correctly create pixels from colors regardless of the underlying pixel format.

Le due righe successive di iOS e Android schermate sono anche gli stessi, a conferma del fatto che il piccolo MakePixel per il valore predefinito è correttamente definito metodo Rgba8888 formato pixel per queste piattaforme.The next two rows of the iOS and Android screenshots are also the same, which confirms that the little MakePixel method is correctly defined for the default Rgba8888 pixel format for these platforms.

La riga nella parte inferiore di iOS e Android schermate con le versioni precedenti, è che indica che l'intero senza segno ottenuto eseguendo il cast di un SKColor valore è nel formato:The bottom row of the iOS and Android screenshots is backwards, which indicates that the unsigned integer obtained by casting an SKColor value is in the form:

AARRGGBBAARRGGBB

I byte sono nell'ordine:The bytes are in the order:

GG BB RR AABB GG RR AA

Questo è il Bgra8888 ordinamento anziché il Rgba8888 ordering.This is the Bgra8888 ordering rather than the Rgba8888 ordering. Il Brga8888 formato è il valore predefinito per la piattaforma Windows universale, motivo per cui le sfumature nell'ultima riga di tale schermata sono gli stessi della prima riga.The Brga8888 format is the default for the Universal Windows platform, which is why the gradients on the last row of that screenshot are the same as the first row. Ma le due righe centrale non sono corrette perché si presuppone che il codice di creazione di tali bitmap un Rgba8888 ordering.But the middle two rows are incorrect because the code creating those bitmaps assumed an Rgba8888 ordering.

Se si desidera usare lo stesso codice per l'accesso ai bit di pixel in ogni piattaforma, è possibile creare in modo esplicito un' SKBitmap usando il Rgba8888 o Bgra8888 formato.If you want to use the same code for accessing pixel bits on each platform, you can explicitly create an SKBitmap using either the Rgba8888 or Bgra8888 format. Se si desidera eseguire il cast SKColor usare i valori per pixel delle bitmap, Bgra8888.If you want to cast SKColor values to bitmap pixels, use Bgra8888.

Accesso casuale di pixelRandom access of pixels

Il FillBitmapBytePtr e FillBitmapUintPtr metodi nel Bitmap sfumatura pagina tratto vantaggi da for cicli progettati per riempire la bitmap in sequenza, dalla prima riga per riga inferiore e in ogni riga da sinistra a destra.The FillBitmapBytePtr and FillBitmapUintPtr methods in the Gradient Bitmap page benefited from for loops designed to fill the bitmap sequentially, from top row to bottom row, and in each row from left to right. Il pixel è stato possibile impostare con la stessa istruzione il puntatore viene incrementato.The pixel could be set with the same statement that incremented the pointer.

In alcuni casi è necessario accedere i pixel in modo casuale anziché in sequenza.Sometimes it's necessary to access the pixels randomly rather than sequentially. Se si usa il GetPixels approccio, è necessario calcolare i puntatori basati sulla riga e colonna.If you're using the GetPixels approach, you'll need to calculate pointers based on the row and column. Questa funzionalità viene illustrata la arcobaleno seno pagina, che crea una bitmap che mostra un arcobaleno sotto forma di un ciclo di una curva seno.This is demonstrated in the Rainbow Sine page, which creates a bitmap showing a rainbow in the form of one cycle of a sine curve.

I colori dell'arcobaleno sono più facili da creare usando il modello di colori HSL (hue, saturation, luminosità).The colors of the rainbow are easiest to create using the HSL (hue, saturation, luminosity) color model. Il SKColor.FromHsl metodo crea un SKColor valore usando i valori di tonalità compresi tra 0 e 360 (ad esempio gli angoli di un cerchio, ma passa da rosso, verde e blu e nuovamente su rosso) e i valori di saturazione e luminosità compreso tra 0 e 100.The SKColor.FromHsl method creates an SKColor value using hue values that range from 0 to 360 (like the angles of a circle but going from red, through green and blue, and back to red), and saturation and luminosity values ranging from 0 to 100. Per i colori di un arcobaleno, impostare la saturazione a un massimo di 100 e la luminosità per un punto medio pari a 50.For the colors of a rainbow, the saturation should be set to a maximum of 100, and the luminosity to a midpoint of 50.

Seno arcobaleno crea l'immagine da scorrendo le righe della bitmap e quindi eseguire i cicli di valori di tonalità 360.Rainbow Sine creates this image by looping through the rows of the bitmap, and then looping through 360 hue values. Da ogni valore di tonalità, e calcola una colonna mappa di bit che si basa inoltre su un valore del seno:From each hue value, it calculates a bitmap column that is also based on a sine value:

public class RainbowSinePage : ContentPage
{
    SKBitmap bitmap;

    public RainbowSinePage()
    {
        Title = "Rainbow Sine";

        bitmap = new SKBitmap(360 * 3, 1024, SKColorType.Bgra8888, SKAlphaType.Unpremul);

        unsafe
        {
            // Pointer to first pixel of bitmap
            uint* basePtr = (uint*)bitmap.GetPixels().ToPointer();

            // Loop through the rows
            for (int row = 0; row < bitmap.Height; row++)
            {
                // Calculate the sine curve angle and the sine value
                double angle = 2 * Math.PI * row / bitmap.Height;
                double sine = Math.Sin(angle);

                // Loop through the hues
                for (int hue = 0; hue < 360; hue++)
                {
                    // Calculate the column
                    int col = (int)(360 + 360 * sine + hue);

                    // Calculate the address
                    uint* ptr = basePtr + bitmap.Width * row + col;

                    // Store the color value
                    *ptr = (uint)SKColor.FromHsl(hue, 100, 50);
                }
            }
        }

        // Create the 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();
        canvas.DrawBitmap(bitmap, info.Rect);
    }
}

Si noti che il costruttore crea la mappa di bit in base il SKColorType.Bgra8888 formato:Notice that the constructor creates the bitmap based on the SKColorType.Bgra8888 format:

bitmap = new SKBitmap(360 * 3, 1024, SKColorType.Bgra8888, SKAlphaType.Unpremul);

Ciò consente al programma di usare la conversione di SKColor i valori in uint pixel estrema semplicità.This allows the program to use the conversion of SKColor values into uint pixels without worry. Anche se non svolgono un ruolo in questo programma specifico, ogni volta che utilizza il SKColor conversione impostare pixel, è necessario specificare anche SKAlphaType.Unpremul perché SKColor non premoltiplicare relativi componenti di colore per il valore alfa.Although it doesn't play a role in this particular program, whenever you use the SKColor conversion to set pixels, you should also specify SKAlphaType.Unpremul because SKColor doesn't premultiply its color components by the alpha value.

Il costruttore utilizza quindi il GetPixels metodo per ottenere un puntatore al primo pixel della bitmap:The constructor then uses the GetPixels method to obtain a pointer to the first pixel of the bitmap:

uint* basePtr = (uint*)bitmap.GetPixels().ToPointer();

Per qualsiasi particolare riga e colonna, un valore di offset deve essere aggiunto a basePtr.For any particular row and column, an offset value must be added to basePtr. Questo offset è la riga moltiplicata per la larghezza della bitmap, nonché la colonna:This offset is the row times the bitmap width, plus the column:

uint* ptr = basePtr + bitmap.Width * row + col;

Il SKColor viene archiviato in memoria con il puntatore ' this ':The SKColor value is stored in memory using this pointer:

*ptr = (uint)SKColor.FromHsl(hue, 100, 50);

Nel PaintSurface gestore del SKCanvasView, la bitmap viene adattata per riempire l'area di visualizzazione:In the PaintSurface handler of the SKCanvasView, the bitmap is stretched to fill the display area:

Seno arcobalenoRainbow Sine

Da una singola bitmap a un'altraFrom one bitmap to another

Un numero molto elevato le attività di elaborazione di immagini includere la modifica pixel come vengono trasferiti da una singola bitmap a altro.Very many image-processing tasks involve modifying pixels as they are transferred from one bitmap to another. Questa tecnica è dimostrata nel regolazione del colore pagina.This technique is demonstrated in the Color Adjustment page. La pagina viene caricata una delle risorse di bitmap e quindi consente di modificare l'immagine utilizzando tre Slider viste:The page loads one of the bitmap resources and then allows you to modify the image using three Slider views:

La regolazione dei coloriColor Adjustment

Per ogni colore del pixel, il primo Slider aggiunge un valore compreso tra 0 e 360, di tonalità, ma usa quindi l'operatore per mantenere il risultato compreso tra 0 e 360, modulo shifting in modo efficace i colori nell'ambito (come illustrato nella schermata UWP).For each pixel color, the first Slider adds a value from 0 to 360 to the hue, but then uses the modulo operator to keep the result between 0 and 360, effectively shifting the colors along the spectrum (as the UWP screenshot demonstrates). La seconda Slider consente di scegliere un fattore di moltiplicazione tra lo 0,5 e 2 da applicare alla saturazione e il terzo Slider esegue la stessa operazione per la luminosità, come illustrato nello screenshot di Android.The second Slider lets you select a multiplicative factor between 0.5 and 2 to apply to the saturation, and the third Slider does the same for the luminosity, as demonstrated in the Android screenshot.

Il programma mantiene due bitmap, bitmap di origine denominata srcBitmap e la bitmap di destinazione modificato denominato dstBitmap.The program maintains two bitmaps, the original source bitmap named srcBitmap and the adjusted destination bitmap named dstBitmap. Ogni volta che un Slider viene spostato, viene calcolato automaticamente tutti i pixel nuovo dstBitmap.Each time a Slider is moved, the program calculates all new pixels in dstBitmap. Naturalmente, gli utenti verranno utilizzate spostando il Slider viste molto rapidamente, in modo che si desiderano ottenere prestazioni ottimali è possibile gestire.Of course, users will experiment by moving the Slider views very quickly, so you want the best performance you can manage. Questa operazione comporta il GetPixels metodo per la bitmap di origine e di destinazione.This involves the GetPixels method for both the source and destination bitmaps.

Il regolazione del colore pagina non di controllare il formato di colore delle bitmap di origine e destinazione.The Color Adjustment page doesn't control the color format of the source and destination bitmaps. Contiene invece leggermente diverso per la logica per SKColorType.Rgba8888 e SKColorType.Bgra8888 formati.Instead, it contains slightly different logic for SKColorType.Rgba8888 and SKColorType.Bgra8888 formats. L'origine e destinazione possono essere formati diversi, e il programma continuerà a funzionare.The source and destination can be different formats, and the program will still work.

Ecco il programma, ad eccezione di essenziale TransferPixels metodo che consente di trasferire i pixel formano l'origine alla destinazione.Here's the program except for the crucial TransferPixels method that transfers the pixels form the source to the destination. Il costruttore imposta dstBitmap uguale a srcBitmap.The constructor sets dstBitmap equal to srcBitmap. Il PaintSurface gestore visualizza dstBitmap:The PaintSurface handler displays dstBitmap:

public partial class ColorAdjustmentPage : ContentPage
{
    SKBitmap srcBitmap =
        BitmapExtensions.LoadBitmapResource(typeof(FillRectanglePage),
                                            "SkiaSharpFormsDemos.Media.Banana.jpg");
    SKBitmap dstBitmap;

    public ColorAdjustmentPage()
    {
        InitializeComponent();

        dstBitmap = new SKBitmap(srcBitmap.Width, srcBitmap.Height);
        OnSliderValueChanged(null, null);
    }

    void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
    {
        float hueAdjust = (float)hueSlider.Value;
        hueLabel.Text = $"Hue Adjustment: {hueAdjust:F0}";

        float saturationAdjust = (float)Math.Pow(2, saturationSlider.Value);
        saturationLabel.Text = $"Saturation Adjustment: {saturationAdjust:F2}";

        float luminosityAdjust = (float)Math.Pow(2, luminositySlider.Value);
        luminosityLabel.Text = $"Luminosity Adjustment: {luminosityAdjust:F2}";

        TransferPixels(hueAdjust, saturationAdjust, luminosityAdjust);
        canvasView.InvalidateSurface();
    }
    ···
    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

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

Il ValueChanged gestore per il Slider viste calcola i valori di regolazione e chiamate TransferPixels.The ValueChanged handler for the Slider views calculates the adjustment values and calls TransferPixels.

L'intera TransferPixels metodo è contrassegnato come unsafe.The entire TransferPixels method is marked as unsafe. Inizia ottenendo i puntatori di byte per i bit di pixel delle bitmap entrambi e quindi scorre in ciclo tutte le righe e colonne.It begins by obtaining byte pointers to the pixel bits of both bitmaps, and then loops through all the rows and columns. Dalla bitmap di origine, il metodo ottiene quattro byte per ogni pixel.From the source bitmap, the method obtains four bytes for each pixel. Questi potrebbero essere in entrambi i Rgba8888 o Bgra8888 ordine.These could be in either the Rgba8888 or Bgra8888 order. Verifica per il tipo di colore consente un SKColor valore deve essere creato.Checking for the color type allows an SKColor value to be created. I componenti HSL sono quindi estratti, modificati e utilizzati per ricreare il SKColor valore.The HSL components are then extracted, adjusted, and used to recreate the SKColor value. A seconda che la bitmap di destinazione sia Rgba8888 o Bgra8888, i byte vengono archiviati in bitmp la destinazione:Depending on whether the destination bitmap is Rgba8888 or Bgra8888, the bytes are stored in the destination bitmp:

public partial class ColorAdjustmentPage : ContentPage
{
    ···
    unsafe void TransferPixels(float hueAdjust, float saturationAdjust, float luminosityAdjust)
    {
        byte* srcPtr = (byte*)srcBitmap.GetPixels().ToPointer();
        byte* dstPtr = (byte*)dstBitmap.GetPixels().ToPointer();

        int width = srcBitmap.Width;       // same for both bitmaps
        int height = srcBitmap.Height;

        SKColorType typeOrg = srcBitmap.ColorType;
        SKColorType typeAdj = dstBitmap.ColorType;

        for (int row = 0; row < height; row++)
        {
            for (int col = 0; col < width; col++)
            {
                // Get color from original bitmap
                byte byte1 = *srcPtr++;         // red or blue
                byte byte2 = *srcPtr++;         // green
                byte byte3 = *srcPtr++;         // blue or red
                byte byte4 = *srcPtr++;         // alpha

                SKColor color = new SKColor();

                if (typeOrg == SKColorType.Rgba8888)
                {
                    color = new SKColor(byte1, byte2, byte3, byte4);
                }
                else if (typeOrg == SKColorType.Bgra8888)
                {
                    color = new SKColor(byte3, byte2, byte1, byte4);
                }

                // Get HSL components
                color.ToHsl(out float hue, out float saturation, out float luminosity);

                // Adjust HSL components based on adjustments
                hue = (hue + hueAdjust) % 360;
                saturation = Math.Max(0, Math.Min(100, saturationAdjust * saturation));
                luminosity = Math.Max(0, Math.Min(100, luminosityAdjust * luminosity));

                // Recreate color from HSL components
                color = SKColor.FromHsl(hue, saturation, luminosity);

                // Store the bytes in the adjusted bitmap
                if (typeAdj == SKColorType.Rgba8888)
                {
                    *dstPtr++ = color.Red;
                    *dstPtr++ = color.Green;
                    *dstPtr++ = color.Blue;
                    *dstPtr++ = color.Alpha;
                }
                else if (typeAdj == SKColorType.Bgra8888)
                {
                    *dstPtr++ = color.Blue;
                    *dstPtr++ = color.Green;
                    *dstPtr++ = color.Red;
                    *dstPtr++ = color.Alpha;
                }
            }
        }
    }
    ···
}

È probabile che le prestazioni di questo metodo può essere ulteriormente migliorati tramite la creazione di metodi separati per le varie combinazioni di tipi di colore delle bitmap di origine e di destinazione ed evitare il controllo del tipo per ogni pixel.It's likely that the performance of this method could be improved even more by creating separate methods for the various combinations of color types of the source and destination bitmaps, and avoid checking the type for every pixel. Un'altra opzione consiste nell'utilizzare più for loop per la col variabile basata sul tipo di colore.Another option is to have multiple for loops for the col variable based on the color type.

PosterizzazionePosterization

È un altro processo comune che prevede l'accesso ai bit di pixel posterizzazione.Another common job that involves accessing pixel bits is posterization. Il numero se colori codificata in pixel della bitmap viene ridotto in modo che il risultato è simile a un poster mano utilizzando una tavolozza dei colori limitato.The number if colors encoded in a bitmap's pixels is reduced so that the result resembles a hand-drawn poster using a limited color palette.

Il Posterizza pagina esegue questo processo su una delle immagini monkey:The Posterize page performs this process on one of the monkey images:

public class PosterizePage : ContentPage
{
    SKBitmap bitmap =
        BitmapExtensions.LoadBitmapResource(typeof(FillRectanglePage),
                                            "SkiaSharpFormsDemos.Media.Banana.jpg");
    public PosterizePage()
    {
        Title = "Posterize";

        unsafe
        {
            uint* ptr = (uint*)bitmap.GetPixels().ToPointer();
            int pixelCount = bitmap.Width * bitmap.Height;

            for (int i = 0; i < pixelCount; i++)
            {
                *ptr++ &= 0xE0E0E0FF;
            }
        }

        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(bitmap, info.Rect, BitmapStretch.Uniform;
    }
}

Il codice nel costruttore accede a ogni pixel, esegue un'operazione con AND bit per bit con il valore 0xE0E0E0FF e quindi archivia il risultato nella bitmap.The code in the constructor accesses each pixel, performs a bitwise AND operation with the value 0xE0E0E0FF, and then stores the result back in the bitmap. I valori 0xE0E0E0FF mantiene 3 bit più significativi di ogni componente del colore e imposta i bit più bassi 5 su 0.The values 0xE0E0E0FF keeps the high 3 bits of each color component and sets the lower 5 bits to 0. Anziché 224 o 16.777.216 colori, la bitmap viene ridotto a 29 o 512 colori:Rather than 224 or 16,777,216 colors, the bitmap is reduced to 29 or 512 colors:

PosterizzazionePosterize