我正在尝试自学C#,并从各种来源获悉,函数获取和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++) { Color c = b.GetPixel(x, y); b.SetPixel(x, y, Color.FromArgb(c.A, f.R, f.G, f.B)); } } 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 SetPixel(int x, int y, Color colour) { int index = x + (y * Width); int col = colour.ToArgb(); Bits[index] = col; } public Color GetPixel(int x, int y) { int index = x + (y * Width); int col = Bits[index]; Color result = Color.FromArgb(col); return result; } public void Dispose() { if (Disposed) return; Disposed = true; Bitmap.Dispose(); BitsHandle.Free(); } }
不需要LockBits或SetPixel。使用上述类可直接访问位图数据。
LockBits
SetPixel
使用此类,可以将原始位图数据设置为32位数据。请注意,它是PARGB,它是预乘alpha。有关如何工作的更多信息,请参见Wikipedia上的Alpha合成,有关BLENDFUNCTION的MSDN文章上的示例,请参阅示例以了解如何正确计算Alpha。
如果预乘可能会使事情复杂化,请PixelFormat.Format32bppArgb改用。绘制时会发生性能下降的情况,因为它是在内部转换为的PixelFormat.Format32bppPArgb。如果在绘制之前不必更改图像,则可以在预乘之前完成工作,将其绘制到PixelFormat.Format32bppArgb缓冲区中,然后从那里进一步使用。
PixelFormat.Format32bppArgb
PixelFormat.Format32bppPArgb
Bitmap通过Bitmap属性公开访问标准成员。使用该Bits属性可以直接访问位图数据。
Bitmap
Bits
byte
int
将的两个实例都更改Int32为byte,然后更改此行:
Int32
Bits = new Int32[width * height];
对此:
Bits = new byte[width * height * 4];
使用字节时,格式为Alpha / Red / Green / Blue。每个像素占用4个字节的数据,每个通道一个。GetPixel和SetPixel函数将需要进行相应的重新设计或删除。
IDisposable
unsafe
Dispose
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)); }
该问题询问性能,因此下面的表格应显示答案中提出的三种不同方法之间的相对性能。这是使用基于.NET Standard 2的应用程序和NUnit完成的。
* Time to fill the entire bitmap with red pixels * - Not including the time to create and dispose the bitmap - Best out of 100 runs taken - Lower is better - Time is measured in Stopwatch ticks to emphasize magnitude rather than actual time elapsed - Tests were performed on an Intel Core i7-4790 based workstation Bitmap size Method 4x4 16x16 64x64 256x256 1024x1024 4096x4096 DirectBitmap <1 2 28 668 8219 178639 LockBits 2 3 33 670 9612 197115 SetPixel 45 371 5920 97477 1563171 25811013 * Test details * - LockBits test: Bitmap.LockBits is only called once and the benchmark includes Bitmap.UnlockBits. It is expected that this is the absolute best case, adding more lock/unlock calls will increase the time required to complete the operation.