位图克隆问题
请考虑以下代码来加载,修改和保存位图图像:
using (Bitmap bmp = new Bitmap("C:\\test.jpg")) { bmp.RotateFlip(RotateFlipType.Rotate180FlipNone); bmp.Save("C:\\test.jpg"); }
它运行没有任何例外。 但考虑一下:
using (Bitmap bmp = new Bitmap("C:\\test.jpg")) { using (Bitmap bmpClone = (Bitmap)bmp.Clone()) { //You can replace "bmpClone" in the following lines with "bmp", //exception occurs anyway bmpClone.RotateFlip(RotateFlipType.Rotate180FlipNone); bmpClone.Save("C:\\test.jpg"); } }
它以ExternalException结束,并显示以下消息:“GDI +中发生了一般错误”。 这有什么不对? 对打开的文件有什么样的锁定? 如果是这样,为什么第一个块有效? 克隆System.Drawing.Bitmap的正确代码是什么,而我们可能需要在内存中编辑主对象或其克隆,并且仍然将它们都加载到内存中?
您还可以使用简单的解决方法加载位图而无需文件锁定:
using (Stream s = File.OpenRead(@"\My Documents\My Pictures\Waterfall.jpg")) Bitmap _backImage = (Bitmap)Bitmap.FromStream(s);
是的,当加载第一个位图对象时,文件被锁定,因此bmpClone.Save()
到同一个文件失败,因为你有一个逻辑死锁。
按文件名打开位图时,文件在Bitmap的整个生命周期内都会被锁定。 如果使用流,则流必须保持打开状态。
更新 :
如果您希望在内存中有两个位图,以便在您所使用的方法范围之外使用,那么您将不会使用using
块。
从文件创建第一个图像,然后克隆它。 在整个UI生命周期中根据需要使用它们,但确保在不再需要Dispose()
时清理它们,以便释放底层资源。
另外,来自MSDN:
不允许将图像保存到它构造的同一文件中并引发exception
那很尴尬。 如果使用clone()
创建的对象保留图像源上的信息(例如原始文件上的句柄),或者在使用Bitmap实例时无法解锁文件,那么您可能需要保存到新的文件,或从原始的临时副本打开。
试试这个:
// ... make a copy of test.jpg called test_temp.jpg Bitmap bmpOriginal = new Bitmap("C:\\test_temp.jpg")) Bitmap bmpClone = (Bitmap)bmp.Clone(); // ... do stuff bmpClone.RotateFlip(RotateFlipType.Rotate180FlipNone); bmpClone.Save("C:\\test.jpg"); // ... cleanup bmpOriginal.Dispose(); bmpClone.Dispose();
这就是我复制位图的方法:
[DllImport("kernel32.dll", EntryPoint = "CopyMemory")] static extern void CopyMemory(IntPtr Destination, IntPtr Source, uint Length); public static Bitmap KernellDllCopyBitmap(Bitmap bmp, bool CopyPalette = true) { Bitmap bmpDest = new Bitmap(bmp.Width, bmp.Height, bmp.PixelFormat); if (!KernellDllCopyBitmap(bmp, bmpDest, CopyPalette)) bmpDest = null; return bmpDest; } /// /// Copy bitmap data. /// Note: bitmaps must have same size and pixel format. /// /// Source Bitmap /// Destination Bitmap /// Must copy Palette public static bool KernellDllCopyBitmap(Bitmap bmpSrc, Bitmap bmpDest, bool CopyPalette = false) { bool copyOk = false; copyOk = CheckCompatibility(bmpSrc, bmpDest); if (copyOk) { BitmapData bmpDataSrc; BitmapData bmpDataDest; //Lock Bitmap to get BitmapData bmpDataSrc = bmpSrc.LockBits(new Rectangle(0, 0, bmpSrc.Width, bmpSrc.Height), ImageLockMode.ReadOnly, bmpSrc.PixelFormat); bmpDataDest = bmpDest.LockBits(new Rectangle(0, 0, bmpDest.Width, bmpDest.Height), ImageLockMode.WriteOnly, bmpDest.PixelFormat); int lenght = bmpDataSrc.Stride * bmpDataSrc.Height; CopyMemory(bmpDataDest.Scan0, bmpDataSrc.Scan0, (uint)lenght); bmpSrc.UnlockBits(bmpDataSrc); bmpDest.UnlockBits(bmpDataDest); if (CopyPalette && bmpSrc.Palette.Entries.Length > 0) bmpDest.Palette = bmpSrc.Palette; } return copyOk; } public static bool CheckCompatibility(Bitmap bmp1, Bitmap bmp2) { return ((bmp1.Width == bmp2.Width) && (bmp1.Height == bmp2.Height) && (bmp1.PixelFormat == bmp2.PixelFormat)); }
## ImageCopyBenchmark ##
图像大小:{宽度= 1024,高度= 1024}。
Image PixelFormat:Format8bppIndexed。
Bitmap.Clone():0,00 ms(不是DeepCopy … 相同的像素数据 – 看这里 )
Bitmap.Clone()+ RotateFlip(以获取深拷贝):2,02毫秒
KernellDllCopyBitmap:0,52毫秒(最棒!)
MarshalCopyBitmap:2,21毫秒
这是我的虚荣方法:
私有静态不安全位图DuplicateBitmap(位图inputBitmap) { byte [] buffer = new byte [inputBitmap.Height * inputBitmap.Width * Image.GetPixelFormatSize(inputBitmap.PixelFormat)/ 8]; 修复(字节* p =缓冲区) { BitmapData b1Data = new BitmapData() { Scan0 =(IntPtr)p, 高度= inputBitmap.Height, 宽度= inputBitmap.Width, PixelFormat = inputBitmap.PixelFormat, Stride = inputBitmap.Width * Image.GetPixelFormatSize(inputBitmap.PixelFormat)/ 8, }; inputBitmap.LockBits(new Rectangle(Point.Empty,inputBitmap.Size), ImageLockMode.ReadOnly | ImageLockMode.UserInputBuffer,inputBitmap.PixelFormat,b1Data); //复制出来 位图b2 =新位图(b1Data.Width,b1Data.Height,b1Data.Stride,inputBitmap.PixelFormat,b1Data.Scan0); inputBitmap.UnlockBits(b1Data); 返回b2; } }
快10%(取决于位图大小……)