【发布时间】:2013-06-10 18:17:14
【问题描述】:
可以直接从解锁的位图非托管内存写入和读取吗?
我可以在解锁 Bitmap 的位后继续使用 BitmapData 吗?我做了一个测试应用程序,我可以在鼠标位置读取 PictureBox 的位图像素,而另一个线程正在将像素写入同一个位图。
编辑 1: 正如Boing 在他的回答中指出的那样:“Scan0 并不指向 Bitmap 对象的实际像素数据;相反,它指向一个临时缓冲区,该缓冲区表示Bitmap 对象中的部分像素数据。”来自MSDN。
但是一旦我得到 Scan0,我就可以在不需要 Lockbits 或 UnlockBits 的情况下读取/写入 Bitmap!我在一个线程中做了很多次。根据 MSDN,它不应该发生,因为 Scan0 指向位图数据的副本!好吧,在 C# 中,所有测试都表明它不是副本。在 C++ 中,我不知道它是否可以正常工作。
编辑 2: 有时使用旋转方法会使操作系统释放位图像素数据副本。结论,@ 987654323@。感谢 Boing 的回答和 cmets!
以下是我如何获取 BitmapData 并读取和写入像素值。
/// <summary>
/// Locks and unlocks the Bitmap to get the BitmapData.
/// </summary>
/// <param name="bmp">Bitmap</param>
/// <returns>BitmapData</returns>
public static BitmapData GetBitmapData(Bitmap bmp)
{
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat);
bmp.UnlockBits(bmpData);
return bmpData;
}
/// <summary>
/// Get pixel directly from unamanged pixel data based on the Scan0 pointer.
/// </summary>
/// <param name="bmpData">BitmapData of the Bitmap to get the pixel</param>
/// <param name="p">Pixel position</param>
/// <param name="channel">Channel</param>
/// <returns>Pixel value</returns>
public static byte GetPixel(BitmapData bmpData, Point p, int channel)
{
if ((p.X > bmpData.Width - 1) || (p.Y > bmpData.Height - 1))
throw new ArgumentException("GetPixel Point p is outside image bounds!");
int bitsPerPixel = ((int)bmpData.PixelFormat >> 8) & 0xFF;
int bpp = bitsPerPixel / 8;
byte data;
int id = p.Y * bmpData.Stride + p.X * bpp;
unsafe
{
byte* pData = (byte*)bmpData.Scan0;
data = pData[id + channel];
}
return data;
}
//Non UI Thread
private void DrawtoBitmapLoop()
{
while (_drawBitmap)
{
_drawPoint = new Point(_drawPoint.X + 10, _drawPoint.Y + 10);
if (_drawPoint.X > _backImageData.Width - 20)
_drawPoint.X = 0;
if (_drawPoint.Y > _backImageData.Height - 20)
_drawPoint.Y = 0;
DrawToScan0(_backImageData, _drawPoint, 1);
Thread.Sleep(10);
}
}
private static void DrawToScan0(BitmapData bmpData, Point start, int channel = 0)
{
int x = start.X;
int y = start.Y;
int bitsPerPixel = ((int)bmpData.PixelFormat >> 8) & 0xFF;
int bpp = bitsPerPixel / 8;
for (int i = 0; i < 10; i++)
{
unsafe
{
byte* p = (byte*)bmpData.Scan0;
int id = bmpData.Stride * y + channel + (x + i) * bpp;
p[id] = 255;
}
}
}
【问题讨论】:
-
对于 c# 中的大图像处理,是的,这是获取和设置像素的一种非常快速的方法,但是如果您正在处理小图像 (GetPixel 和
SetPixel会容易很多 -
@Mehran,请看一下我的编辑和 Boings 的回答。您是否注意到我正在读取/写入未锁定的位图?因此,对于 MSDN,这应该行不通。
-
我尝试了你所说的,但没有解锁位,我在
picturebox中得到一个白色背景的红十字,并且没有图像! ,正如您已经提到的那样,这不应该起作用,但我不知道为什么在您的情况下它会起作用! -
@Mehran,你看过方法“public static BitmapData GetBitmapData(Bitmap bmp)”吗?好吧,正如你所见,我解锁了它。不解锁我会像你一样得到一个红十字会。 1. 创建位图。 2. 获取位图数据。 3. 将位图添加到 PictureBox 4. 对位图进行任何操作(读/写),结果将显示在 PictureBox 中。 :)
标签: c# bitmap lockbits unmanaged-memory