Voici comment je manipulerais des pixels en C# en utilisant des tableaux multidimensionnels :
[StructLayout(LayoutKind.Sequential)]
public struct PixelColor
{
public byte Blue;
public byte Green;
public byte Red;
public byte Alpha;
}
public PixelColor[,] GetPixels(BitmapSource source)
{
if(source.Format!=PixelFormats.Bgra32)
source = new FormatConvertedBitmap(source, PixelFormats.Bgra32, null, 0);
int width = source.PixelWidth;
int height = source.PixelHeight;
PixelColor[,] result = new PixelColor[width, height];
source.CopyPixels(result, width * 4, 0);
return result;
}
l'usage :
var pixels = GetPixels(image);
if(pixels[7, 3].Red > 4)
{
...
}
Si vous voulez mettre à jour les pixels, un code très similaire fonctionne, sauf que vous créerez un fichier de type WriteableBitmap
et utiliser ceci :
public void PutPixels(WriteableBitmap bitmap, PixelColor[,] pixels, int x, int y)
{
int width = pixels.GetLength(0);
int height = pixels.GetLength(1);
bitmap.WritePixels(new Int32Rect(0, 0, width, height), pixels, width*4, x, y);
}
ainsi :
var pixels = new PixelColor[4, 3];
pixels[2,2] = new PixelColor { Red=128, Blue=0, Green=255, Alpha=255 };
PutPixels(bitmap, pixels, 7, 7);
Notez que ce code convertit les bitmaps en Bgra32 s'ils arrivent dans un format différent. C'est généralement rapide, mais dans certains cas, cela peut être un goulot d'étranglement, auquel cas cette technique serait modifiée pour correspondre plus étroitement au format d'entrée sous-jacent.
Mise à jour
Depuis BitmapSource.CopyPixels
n'accepte pas un tableau à deux dimensions, il est nécessaire de convertir le tableau entre une et deux dimensions. La méthode d'extension suivante devrait faire l'affaire :
public static class BitmapSourceHelper
{
#if UNSAFE
public unsafe static void CopyPixels(this BitmapSource source, PixelColor[,] pixels, int stride, int offset)
{
fixed(PixelColor* buffer = &pixels[0, 0])
source.CopyPixels(
new Int32Rect(0, 0, source.PixelWidth, source.PixelHeight),
(IntPtr)(buffer + offset),
pixels.GetLength(0) * pixels.GetLength(1) * sizeof(PixelColor),
stride);
}
#else
public static void CopyPixels(this BitmapSource source, PixelColor[,] pixels, int stride, int offset)
{
var height = source.PixelHeight;
var width = source.PixelWidth;
var pixelBytes = new byte[height * width * 4];
source.CopyPixels(pixelBytes, stride, 0);
int y0 = offset / width;
int x0 = offset - width * y0;
for(int y=0; y<height; y++)
for(int x=0; x<width; x++)
pixels[x+x0, y+y0] = new PixelColor
{
Blue = pixelBytes[(y*width + x) * 4 + 0],
Green = pixelBytes[(y*width + x) * 4 + 1],
Red = pixelBytes[(y*width + x) * 4 + 2],
Alpha = pixelBytes[(y*width + x) * 4 + 3],
};
}
#endif
}
Il y a deux mises en œuvre ici : La première est rapide mais utilise du code non sécurisé pour obtenir un IntPtr vers un tableau (doit être compilé avec l'option /unsafe). La seconde est plus lente mais ne nécessite pas de code non sécurisé. J'utilise la version unsafe dans mon code.
WritePixels accepte les tableaux bidimensionnels, donc aucune méthode d'extension n'est nécessaire.
Modification : comme Jerry l'a fait remarquer dans les commentaires, en raison de la disposition de la mémoire, le tableau bidimensionnel a la coordonnée verticale en premier, en d'autres termes, il doit être dimensionné en tant que Pixels[Hauteur,Largeur] et non Pixels[Largeur,Hauteur] et adressé en tant que Pixels[y,x].
0 votes
A quoi sert le nStride ? Et pourquoi ajouter 7 et diviser par 8 dans le calcul de nStride ?
3 votes
@Jviaches Ajoutez 7 et divisez par 8 pour arrondir correctement à un nombre suffisant d'octets (par exemple, 10 bits nécessitent 2 octets).