Bitmap.Clone()和新的Bitmap(Bitmap)有什么区别?

据我所知,复制位图有两种方法。

Bitmap.Clone()

Bitmap A = new Bitmap("somefile.png"); Bitmap B = (Bitmap)A.Clone(); 

新的位图()

 Bitmap A = new Bitmap("somefile.png"); Bitmap B = new Bitmap(A); 

这些方法如何不同? 我特别感兴趣的是内存和线程方面的差异。

这是“深”和“浅”副本之间的共同之处,也是几乎不赞成的IClonable接口的问题。 Clone()方法创build一个新的Bitmap对象,但像素数据与原始位图对象共享。 位图(图像)构造函数也创build一个新的位图对象,但有一个自己的像素数据的副本。

使用克隆()是非常有用的。 很多关于它的问题,程序员希望Clone()避免了位图的典型问题,即加载文件的锁。 它不。 当您传递一个引用来放置位图的代码时,只使用Clone(),而不想丢失该对象。

阅读以前的答案,我担心像素数据将在克隆的位图实例之间共享。 所以我执行了一些testing来找出Bitmap.Clone()new Bitmap()之间的区别。

Bitmap.Clone()保持原始文件被locking:

  Bitmap original = new Bitmap("Test.jpg"); Bitmap clone = (Bitmap) original.Clone(); original.Dispose(); File.Delete("Test.jpg"); // Will throw System.IO.IOException 

使用new Bitmap(original)而不是在original.Dispose()后解锁文件,并不会抛出exception。 使用Graphics类修改克隆(使用.Clone()创build)不会修改原始:

  Bitmap original = new Bitmap("Test.jpg"); Bitmap clone = (Bitmap) original.Clone(); Graphics gfx = Graphics.FromImage(clone); gfx.Clear(Brushes.Magenta); Color c = original.GetPixel(0, 0); // Will not equal Magenta unless present in the original 

同样,使用LockBits方法为原始和克隆生成不同的内存块:

  Bitmap original = new Bitmap("Test.jpg"); Bitmap clone = (Bitmap) original.Clone(); BitmapData odata = original.LockBits(new Rectangle(0, 0, original.Width, original.Height), ImageLockMode.ReadWrite, original.PixelFormat); BitmapData cdata = clone.LockBits(new Rectangle(0, 0, clone.Width, clone.Height), ImageLockMode.ReadWrite, clone.PixelFormat); Assert.AreNotEqual(odata.Scan0, cdata.Scan0); 

结果与object ICloneable.Clone()Bitmap Bitmap.Clone(Rectangle, PixelFormat)

接下来,我使用下面的代码尝试了一些简单的基准testing。

在列表中存储50个副本需要6.2秒,导致1.7 GB的内存使用(原始图像是24 bpp和3456 x 2400像素= 25 MB):

  Bitmap original = new Bitmap("Test.jpg"); long mem1 = Process.GetCurrentProcess().PrivateMemorySize64; Stopwatch timer = Stopwatch.StartNew(); List<Bitmap> list = new List<Bitmap>(); Random rnd = new Random(); for(int i = 0; i < 50; i++) { list.Add(new Bitmap(original)); } long mem2 = Process.GetCurrentProcess().PrivateMemorySize64; Debug.WriteLine("ElapsedMilliseconds: " + timer.ElapsedMilliseconds); Debug.WriteLine("PrivateMemorySize64: " + (mem2 - mem1)); 

使用Clone()而不是我可以在0.7秒内存储1 000 000份副本,并使用0.9 GB。 正如所料,与new Bitmap()相比, Clone()的重量非常轻:

  for(int i = 0; i < 1000000; i++) { list.Add((Bitmap) original.Clone()); } 

使用Clone()方法的Clone()是copy-on-write。 在这里,我将一个随机像素更改为克隆上的随机颜色。 这个操作似乎触发了原始的所有像素数据的副本,因为我们现在已经回到了7.8秒和1.6 GB:

  Random rnd = new Random(); for(int i = 0; i < 50; i++) { Bitmap clone = (Bitmap) original.Clone(); clone.SetPixel(rnd.Next(clone.Width), rnd.Next(clone.Height), Color.FromArgb(rnd.Next(0x1000000))); list.Add(clone); } 

从图像中创build一个Graphics对象不会触发这个拷贝:

  for(int i = 0; i < 50; i++) { Bitmap clone = (Bitmap) original.Clone(); Graphics.FromImage(clone).Dispose(); list.Add(clone); } 

您必须使用Graphics对象绘制某些东西才能触发副本。 最后,另一方面使用LockBits ,即使指定了ImageLockMode.ReadOnly也会复制数据:

  for(int i = 0; i < 50; i++) { Bitmap clone = (Bitmap) original.Clone(); BitmapData data = clone.LockBits(new Rectangle(0, 0, clone.Width, clone.Height), ImageLockMode.ReadOnly, clone.PixelFormat); clone.UnlockBits(data); list.Add(clone); }