位图克隆问题

请考虑以下代码来加载,修改和保存位图图像:

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%(取决于位图大小……)