这个问题是关于如何读取/写入,分配和管理位图的像素数据。
这是一个如何为像素数据分配字节数组(托管内存)并使用它创建位图的示例:
Size size = new Size(800, 600);
PixelFormat pxFormat = PixelFormat.Format8bppIndexed;
//Get the stride, in this case it will have the same length of the width.
//Because the image Pixel format is 1 Byte/pixel.
//Usually stride = "ByterPerPixel"*Width
//但这并不总是正确的。 More info at bobpowell。
int stride = GetStride(size.Width, pxFormat);
byte[] data = new byte[stride * size.Height];
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
Bitmap bmp = new Bitmap(size.Width, size.Height, stride,
pxFormat, handle.AddrOfPinnedObject());
//After doing your stuff, free the Bitmap and unpin the array.
bmp.Dispose();
handle.Free();
public static int GetStride(int width, PixelFormat pxFormat)
{
//float bitsPerPixel = System.Drawing.Image.GetPixelFormatSize(format);
int bitsPerPixel = ((int)pxFormat >> 8) & 0xFF;
//Number of bits used to store the image data per line (only the valid data)
int validBitsPerLine = width * bitsPerPixel;
//4 bytes for every int32 (32 bits)
int stride = ((validBitsPerLine + 31) / 32) * 4;
return stride;
}
我以为该位图可以复制数组数据,但实际上它指向相同的数据。您可以看到:
Color c;
c = bmp.GetPixel(0, 0);
Console.WriteLine("Color before: " + c.ToString());
//Prints: Color before: Color [A=255, R=0, G=0, B=0]
data[0] = 255;
c = bmp.GetPixel(0, 0);
Console.WriteLine("Color after: " + c.ToString());
//Prints: Color after: Color [A=255, R=255, G=255, B=255]
问题:
编辑1:一些后续
您也可以使用元帅来分配内存,也可以使用位图分配的非托管内存。
85,000 bytes
的分配都将在large object heap (LOH)
上进行……”“仅在第二代收集期间收集LOH”。 .NET 4.5的LOH有所改进。 Boing
或其他人可以使用please clarify it
,我将很高兴。顺便说一句,为什么我不能只是directly read/write to Sca0 without locking? =>您不应直接写入Scan0,因为Scan0指向非托管内存(在GDI内部)生成的位图数据的副本。解锁后,该内存可以重新分配给其他内容,不再确定Scan0会指向实际的位图数据。可以将Scan0锁定,解锁并在解锁的位图中进行一些旋转平移,以重现这一点。一段时间后,Scan0将指向无效的区域,并且尝试读取/写入其内存位置时会遇到异常。 最佳答案
从数组创建灰度位图的示例代码:
var b = new Bitmap(Width, Height, PixelFormat.Format8bppIndexed);
ColorPalette ncp = b.Palette;
for (int i = 0; i < 256; i++)
ncp.Entries[i] = Color.FromArgb(255, i, i, i);
b.Palette = ncp;
var BoundsRect = new Rectangle(0, 0, Width, Height);
BitmapData bmpData = b.LockBits(BoundsRect,
ImageLockMode.WriteOnly,
b.PixelFormat);
IntPtr ptr = bmpData.Scan0;
int bytes = bmpData.Stride*b.Height;
var rgbValues = new byte[bytes];
// fill in rgbValues, e.g. with a for loop over an input array
Marshal.Copy(rgbValues, 0, ptr, bytes);
b.UnlockBits(bmpData);
return b;