ser*_*0ne 2 .net c# gdi+ pointers unsafe
那里有一些我不喜欢的代码,因为它不安全,我想知道它是否可以安全:
public static bool Invert(Bitmap b)
{
// GDI+ still lies to us - the return format is BGR, NOT RGB.
BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height),
ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
int stride = bmData.Stride;
System.IntPtr Scan0 = bmData.Scan0;
unsafe
{
byte * p = (byte *)(void *)Scan0;
int nOffset = stride - b.Width*3;
int nWidth = b.Width * 3;
for(int y=0;y < b.Height;++y)
{
for(int x=0; x < nWidth; ++x )
{
p[0] = (byte)(255-p[0]);
++p;
}
p += nOffset;
}
}
b.UnlockBits(bmData);
return true;
}
Run Code Online (Sandbox Code Playgroud)
这条线byte* p = (byte*)(void*)Scan0;看起来像罪魁祸首,但我不得不说我真的不明白它在做什么,或者它是如何安全的.
有人可以对此有所了解吗?
出于性能原因,主要使用不安全的代码.基本思想是你在图像数据上逐字节地移动,并手动翻转每个字节(尽管有更有效和简单的方法来处理同样的事情).
底层映像由GDI +处理,这是非托管代码.因此,当您直接使用图像字节时,必须操纵非托管内存.实际上,安全性或不安全性确实令人惊讶地难以确定 - 它在很大程度上取决于最初如何分配非托管内存.鉴于您正在使用托管代码,并且您可能从文件或某些流中加载了位图,实际上它很可能不是非常不安全,实际上 - 例如,您无法意外覆盖托管内存.该unsafe关键字的名字并非来自被内在的危险-它来自允许你做非常不安全的事情.例如,如果您在自己的托管堆栈上为位图分配了内存,那么您可能会把事情搞得一团糟.
总的来说,如果你能真正证明它是值得的,那么只使用不安全的代码是一个好习惯.在图像处理中,这通常是一个很好的权衡 - 你正在处理大量的简单数据,例如边界检查中的开销可能很大,即使很容易只验证它们一次,而不是在每次迭代中周期.
如果你想摆脱这个不安全的代码,一种方法是分配你自己的byte[](托管),用于Marshal.Copy将图像数据复制到此byte[],在托管数组中进行修改,然后Marshal.Copy再次使用复制结果.问题是,这意味着分配一个byte[]与原始图像一样大的,然后将其复制两次(在这种情况下,边界检查可以忽略不计 - .NET JIT编译器将对其进行优化).最后,在使用它时仍然可能会犯错误,Marshal.Copy这会给你带来与你一样的问题unsafe(不完全是,但这将是一个更长的谈话).
对我而言,unsafe作为关键字的最有价值的部分是,它允许您本地化您正在做的不安全的东西.虽然典型的非托管应用程序一直不安全,但C#只允许您在代码的特定标记部分中不安全.虽然这些仍然会影响代码的其余部分(这是您只能unsafe在FullTrust环境中使用的原因之一),但它使调试和控制更容易.这是一如既往的权衡.
但是,代码实际上是以一种非常不同的方式不安全的 - UnlockBits如果代码中间有异常,则调用可能永远不会发生.您应该使用finally子句来确保推进者清理非托管资源.
最后一点,如果你想要"真正的"性能,安全或不安全,你可能无论如何都不会在CPU上进行图像处理.今天,通常可以安全地假设您运行的计算机具有GPU,可以更快,更轻松地完成工作,并完全隔离计算机本身实际运行的代码.