pur*_*doo 37 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++)
{
Color c = b.GetPixel(x, y);
b.SetPixel(x, y, Color.FromArgb(c.A, f.R, f.G, f.B));
}
}
return b;
}
Run Code Online (Sandbox Code Playgroud)
Sax*_*ike 82
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();
}
}
Run Code Online (Sandbox Code Playgroud)
没有必要LockBits或SetPixel.使用上面的类直接访问位图数据.
使用此类,可以将原始位图数据设置为32位数据.请注意,它是PARGB,它是预乘alpha.有关其工作原理的详细信息,请参阅维基百科上的Alpha Compositing,以及有关如何正确计算alpha 的MSDN文章中有关BLENDFUNCTION的示例.
如果预乘可能使事情过于复杂,请PixelFormat.Format32bppArgb改用.在绘制时会发生性能损失,因为它在内部被转换为PixelFormat.Format32bppPArgb.如果图像在绘制之前不必更改,则可以在预乘之前完成工作,绘制到PixelFormat.Format32bppArgb缓冲区,并从那里进一步使用.
Bitmap通过Bitmap酒店可以使用标准会员.使用Bits属性直接访问位图数据.
byte而不是int原始像素数据将两个实例更改Int32为byte,然后更改此行:
Bits = new Int32[width * height];
Run Code Online (Sandbox Code Playgroud)
对此:
Bits = new byte[width * height * 4];
Run Code Online (Sandbox Code Playgroud)
使用字节时,格式为Alpha/Red/Green/Blue.每个像素占用4个字节的数据,每个通道一个.GetPixel和SetPixel函数需要相应地重新处理或删除.
IDisposable就像Bitmap.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));
}
Run Code Online (Sandbox Code Playgroud)
这个问题询问了性能,所以这里有一个表格,应该显示答案中提出的三种不同方法之间的相对表现.这是使用基于.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.
Run Code Online (Sandbox Code Playgroud)
Bor*_*ort 16
位图操作在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)
{
}
}
Run Code Online (Sandbox Code Playgroud)
小智 6
已经有一段时间了,但我发现了一个可能有用的例子。
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);
Run Code Online (Sandbox Code Playgroud)
其中btm是位图变量。