C# – 更快的替代SetPixel和GetPixel的Windows窗体应用程序位图

我想教自己的C#,并从各种各样的消息来源得知,函数获取和setpixel可以是非常缓慢。 什么是一些替代scheme,性能改进真的很重要? 提前致谢!

我的代码的一大块供参考:

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(cA, fR, fG, fB)); } } return b; } 

即时可用的代码

 public class DirectBitmap : IDisposable { public Bitmap Bitmap { get; private set; } public Int32[] Bits { get; private set; } public bool Disposed { get; private set; } public int Height { get; private set; } public int Width { get; private set; } protected GCHandle BitsHandle { get; private set; } public DirectBitmap(int width, int height) { Width = width; Height = height; Bits = new Int32[width * height]; BitsHandle = GCHandle.Alloc(Bits, GCHandleType.Pinned); Bitmap = new Bitmap(width, height, width * 4, PixelFormat.Format32bppPArgb, BitsHandle.AddrOfPinnedObject()); } public void Dispose() { if (Disposed) return; Disposed = true; Bitmap.Dispose(); BitsHandle.Free(); } } 

不需要LockBitsSetPixel 。 使用上述类直接访问位图数据。

使用这个类,可以将原始位图数据设置为32位数据。 请注意,它是预乘alpha的PARGB。 请参阅维基百科上的Alpha合成关于如何工作的更多信息以及MSDN文章中用于BLENDFUNCTION的示例,以了解如何正确计算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 / Red / Green / Blue。 每个像素需要4个字节的数据,每个通道一个。

使用上述类的好处

  • 仅仅处理数据的内存分配是不必要的。 对原始数据所做的更改将立即应用于位图。
  • 没有其他的对象可以pipe理。 这就像Bitmap一样实现了IDisposable
  • 它不需要一个unsafe块。

注意事项

  • 固定的内存不能移动。 这是为了使这种内存访问起作用所必需的副作用。 这降低了垃圾收集器的效率( MSDN文章 )。 只需要在需要性能的位图上进行操作,并确保在完成操作后将其解除,以便可以取消固定内存。

通过Graphics对象访问

由于Bitmap属性实际上是一个.NET Bitmap对象,因此使用Graphics类执行操作非常简单。

 var dbm = new DirectBitmap(200, 200); using (var g = Graphics.FromImage(dbm.Bitmap)) { g.DrawRectangle(Pens.Black, new Rectangle(50, 50, 100, 100)); } 

C#中位图操作的原因很慢是由于locking和解锁。 每个操作将对所需的位执行locking,操作位,然后解锁位。

您可以通过自己处理操作大大提高速度。 看下面的例子。

 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) { } } 

您可以使用Bitmap.LockBits方法。 另外如果你想使用并行任务执行,你可以在System.Threading.Tasks命名空间中使用Parallel类。 以下链接有一些样本和解释。