C#-Windows窗体位图应用程序的SetPixel和GetPixel的更快替代方案
我试着自学C#,并且从各种来源听说函数get和setpixel的速度非常慢。有哪些替代方案?性能改进是否真的那么重要?提前谢谢 我的一段代码供参考:C#-Windows窗体位图应用程序的SetPixel和GetPixel的更快替代方案,c#,drawing,gdi+,getpixel,C#,Drawing,Gdi+,Getpixel,我试着自学C#,并且从各种来源听说函数get和setpixel的速度非常慢。有哪些替代方案?性能改进是否真的那么重要?提前谢谢 我的一段代码供参考: public static Bitmap Paint(Bitmap _b, Color f) { Bitmap b = new Bitmap(_b); for (int x = 0; x < b.Width; x++) { for (int y = 0; y < b.Height; y++) {
public static Bitmap Paint(Bitmap _b, Color f)
{
Bitmap b = new Bitmap(_b);
for (int x = 0; x < b.Width; x++)
{
for (int y = 0; y < b.Height; y++)
{
Color c = b.GetPixel(x, y);
b.SetPixel(x, y, Color.FromArgb(c.A, f.R, f.G, f.B));
}
}
return b;
}
公共静态位图绘制(位图b,颜色f)
{
位图b=新位图(_b);
对于(int x=0;x
在C#中位图操作如此缓慢的原因在于锁定和解锁。每次操作都会对所需位执行锁定、操作位,然后解锁位
通过自己处理操作,您可以大大提高速度。请参见下面的示例
using (var tile = new Bitmap(tilePart.Width, tilePart.Height))
{
try
{
BitmapData srcData = sourceImage.LockBits(tilePart, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
BitmapData dstData = tile.LockBits(new Rectangle(0, 0, tile.Width, tile.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
unsafe
{
byte* dstPointer = (byte*)dstData.Scan0;
byte* srcPointer = (byte*)srcData.Scan0;
for (int i = 0; i < tilePart.Height; i++)
{
for (int j = 0; j < tilePart.Width; j++)
{
dstPointer[0] = srcPointer[0]; // Blue
dstPointer[1] = srcPointer[1]; // Green
dstPointer[2] = srcPointer[2]; // Red
dstPointer[3] = srcPointer[3]; // Alpha
srcPointer += BytesPerPixel;
dstPointer += BytesPerPixel;
}
srcPointer += srcStrideOffset + srcTileOffset;
dstPointer += dstStrideOffset;
}
}
tile.UnlockBits(dstData);
aSourceImage.UnlockBits(srcData);
tile.Save(path);
}
catch (InvalidOperationException e)
{
}
}
使用(var tile=新位图(tilePart.Width、tilePart.Height))
{
尝试
{
BitmapData srcData=sourceImage.LockBits(tilePart、ImageLockMode.ReadOnly、PixelFormat.Format32bppArgb);
BitmapData dstData=tile.LockBits(新矩形(0,0,tile.Width,tile.Height),ImageLockMode.ReadWrite,PixelFormat.Format32bppArgb);
不安全的
{
字节*dstPointer=(字节*)dstData.Scan0;
字节*srcPointer=(字节*)srcData.Scan0;
对于(int i=0;i
您可以使用Bitmap.LockBits方法。另外,如果要使用并行任务执行,可以在System.Threading.Tasks命名空间中使用并行类。下面的链接有一些示例和说明
锁位
或设置像素
。使用上述类直接访问位图数据
使用此类,可以将原始位图数据设置为32位数据。请注意,它是PARGB,是alpha的预乘。有关如何工作以及如何正确计算alpha的更多信息,请参阅
如果预乘法可能会使事情过于复杂,请改用PixelFormat.Format32bppArgb
。绘制时会出现性能问题,因为它在内部被转换为PixelFormat.Format32bppPArgb
。如果图像在绘制之前不必更改,则可以在预乘之前完成工作,绘制到PixelFormat.Format32bppArgb
缓冲区,并从那里进一步使用
通过Bitmap
属性公开对标准Bitmap
成员的访问。位图数据可以使用Bits
属性直接访问
对原始像素数据使用byte
而不是int
将Int32
的两个实例更改为byte
,然后更改此行:
Bits = new Int32[width * height];
为此:
Bits = new byte[width * height * 4];
使用字节时,格式按该顺序为Alpha/红色/绿色/蓝色。每个像素需要4个字节的数据,每个通道一个字节。GetPixel和SetPixel函数需要相应地重新处理或删除
使用上述类的好处
- 仅用于操作数据的内存分配是不必要的;对原始数据所做的更改将立即应用于位图
- 没有其他要管理的对象。这实现了与位图类似的
IDisposable
- 它不需要
不安全的
块
- 固定内存无法移动。这是一个必要的副作用,以便这种内存访问工作。这会降低垃圾收集器()的效率。仅在需要性能的情况下使用位图,并确保在完成此操作后
它们,以便可以取消固定内存Dispose
图形
对象访问
由于Bitmap
属性实际上是一个.NETBitmap
对象,因此使用Graphics
类执行操作非常简单
var dbm = new DirectBitmap(200, 200);
using (var g = Graphics.FromImage(dbm.Bitmap))
{
g.DrawRectangle(Pens.Black, new Rectangle(50, 50, 100, 100));
}
性能比较
这个问题是关于性能的,所以这里有一个表格可以显示答案中提出的三种不同方法之间的相对性能。这是使用基于.NET标准2的应用程序和NUnit完成的
*是时候用红色像素填充整个位图了*
-不包括创建和处理位图的时间
-100次跑步中的最佳成绩
-越低越好
-时间是用秒表刻度来测量的,以强调大小而不是实际经过的时间
-测试在基于Intel Core i7-4790的工作站上进行
位图大小
方法4x4 16x16 64x64 256x256 1024 4096x4096
DirectBitmap已经有一段时间了,但我发现了一个可能有用的示例
BitmapData BtmpDt = a.LockBits(new Rectangle(0, 0, btm.Width, btm.Height), ImageLockMode.ReadWrite, btm.PixelFormat);
IntPtr pointer = BtmDt.Scan0;
int size = Math.Abs(BtmDt.Stride) * btm.Height;
byte[] pixels = new byte[size];
Marshal.Copy(pointer, pixels, 0, size);
for (int b = 0; b < pixels.Length; b++)
{
pixels[b] = 255; //Do something here
}
Marshal.Copy(pixels, 0, pointer, size);
btm.UnlockBits(BtmDt);
BitmapData BtmpDt=a.LockBits(新矩形(0,0,btm.Width,btm.Height),ImageLockMode.ReadWrite,btm.PixelFormat);
IntPtr pointer=BtmDt.Scan0;
int size=数学绝对值(BtmDt.步幅)*btm.高度;
字节[]像素=新字节[大小];
复制(指针,像素,0,大小);
用于(int b=0;b
其中btm
BitmapData BtmpDt = a.LockBits(new Rectangle(0, 0, btm.Width, btm.Height), ImageLockMode.ReadWrite, btm.PixelFormat);
IntPtr pointer = BtmDt.Scan0;
int size = Math.Abs(BtmDt.Stride) * btm.Height;
byte[] pixels = new byte[size];
Marshal.Copy(pointer, pixels, 0, size);
for (int b = 0; b < pixels.Length; b++)
{
pixels[b] = 255; //Do something here
}
Marshal.Copy(pixels, 0, pointer, size);
btm.UnlockBits(BtmDt);