更新
我想我会改进这个答案以供将来参考。
将像素存储为无符号 32 位整数数组(C#)会更方便(并且性能更高uint
)。这允许一次性设置每个像素的 4 个通道(alpha、红色、绿色和蓝色)。如果您对使用 alpha 通道(用于透明度)不感兴趣,请参阅下面代码片段中有关像素格式的注释。
您现在表示颜色的方式是 32 位数字,例如0xff0000
,您可能会认为它类似于 CSS 十六进制颜色值。
// Image dimensions.
var width = 640;
var height = 480;
// Array to contain pixel data, with each element representing one pixel.
var pixelBuffer = new uint[width * height];
// When creating the bitmap, the operating system needs to be told the memory
// address of the pixel data array created above. This memory address can be
// found using a pinned GC handle, which also tells the GC (garbage collector)
// that it cannot move the array in memory after this point (as that would
// change the memory address).
//
// Note that the handle prevents the GC from freeing the object automatically,
// so remember to call the handle's `Free` method once finished (see below).
var pixelBufferHandle = GCHandle.Alloc(pixelBuffer, GCHandleType.Pinned);
// Create the bitmap using a constructor that takes a pointer to the array of
// pixel data:
//
// The `width` and `height` parameters are the image dimensions in pixels.
// These should match the values used above.
//
// The `stride` parameter specifies the number of _bytes_ required for each row
// of image data. Calculate this by multiplying the image's width by the number
// of bytes per pixel (here that's `sizeof(uint)`).
//
// The `format` parameter specifies how the colour data of each pixel is stored
// in the pixel data array:
// - For 32-bit RGB colour, use `Format32bppPArgb`. This signifies that the
// alpha channel is present (i.e. each pixel is 4 bytes) but should be
// ignored. Each pixel will be 100% opaque, and the alpha can be zero.
// - For 32-bit ARGB colour, use `Format32bppArgb`. Each pixel will be drawn
// opaque, semi-transparent, or fully transparent based on the alpha.
//
// The `scan0` parameter takes a pointer to the pixel data - use the GC handle
// created above to acquire the address of the array object.
var bitmap = new Bitmap(
width: width,
height: height,
stride: width * sizeof(uint),
format: PixelFormat.Format32bppPArgb,
scan0: pixelBufferHandle.AddrOfPinnedObject()
);
// At this point, the pixel data array can be freely manipulated independently
// of the bitmap. Each time the bitmap is draw, it will reflect the current
// content of the pixel data array.
// Once finished...
// Free the GC handle, allowing the GC to free the object automatically.
gchPixels.Free();
您可以创建用于在特定坐标读取和写入像素的函数:
uint GetPixel(int x, int y) => pixels[x + y * width];
void SetPixel(int x, int y, uint argb) => pixels[x + y * width] = argb;
正如starbeamrainbowlabs指出的那样:
GCHandle
、Bitmap
和PixelFormat
分别位于System.Runtime.InteropServices
、System.Drawing
和System.Drawing.Imaging
中。
下面的原始答案...
有一种很好的方法可以做到这一点,但它要求您使用数组或字节而不是列表。
考虑一下:
// Define the width and the height of the image
int width = 100;
int height = 100;
/// Create an array of bytes consisting of the area's worth of pixels
/// with 3 bytes of data each.
byte[] pixels = new byte[width * height * 3];
/* ... Code for making the image etc. here ... */
// Create the bitmap.
// GCHandle requires System.Runtime.InteropServices
/// PixelFormat requires System.Drawing.Imaging.
Bitmap bmp = new Bitmap(width, height, width * 3, PixelFormat.Format24bppRgb,
GCHandle.Alloc(pixels, GCHandleType.Pinned).AddrOfPinnedObject());
如果要将其更改为 ARGB,只需将“3”的所有实例更改为“4”,然后使用 PixelFormat.Format32bppArgb。
正如我所说,这要求您使用数组而不是列表,但只要您知道图像的宽度和高度,这应该不会太难。在数组中设置单个像素的一个好方法是(也许为此制定一个方法):
pixels[x + y * width] = R;
pixels[x + y * width + 1] = G;
pixels[x + y * width + 2] = B;
如果您正在做的任何事情都需要这种方法,那么这种方法通常也很快。