C# Bitmap.Clone() 和 new Bitmap(Bitmap) 有什么区别?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12709360/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 00:10:31  来源:igfitidea点击:

What's the difference between Bitmap.Clone() and new Bitmap(Bitmap)?

c#bitmapclone

提问by Tom Wright

As far as I can tell, there are two ways of copying a bitmap.

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

Bitmap.Clone()

Bitmap.Clone()

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

new Bitmap()

新位图()

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

How do these approaches differ? I'm particularly interested in the difference in terms of memory and threading.

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

采纳答案by Hans Passant

It is the common difference between a "deep" and a "shallow" copy, also an issue with the almost-deprecated IClonable interface. The Clone() method creates a new Bitmap object but the pixel data is shared with the original bitmap object. The Bitmap(Image) constructor also creates a new Bitmap object but one that has its own copy of the pixel data.

这是“深”和“浅”副本之间的共同区别,也是几乎不推荐使用的 IClonable 接口的一个问题。Clone() 方法创建一个新的 Bitmap 对象,但像素数据与原始位图对象共享。Bitmap(Image) 构造函数还创建了一个新的 Bitmap 对象,但该对象拥有自己的像素数据副本。

Lots of questions about Clone() at SO where the programmer hopes that it avoids the typical trouble with bitmaps, the lock on the file from which it was loaded. It doesn't. A possibly practical usage is avoiding trouble with a library method that inappropriately calls Dispose() on a passed bitmap.

很多关于 Clone() 的问题在 SO 上,程序员希望它避免位图的典型问题,即加载它的文件上的锁定。它没有。一个可能的实际用法是避免库方法在传递的位图上不恰当地调用 Dispose() 的麻烦。

The overloads may be useful, taking advantage of the pixel format conversion or the cropping options.

利用像素格式转换或裁剪选项,重载可能很有用。

回答by Anlo

Reading the previous answers, I got worried that the pixel data would be shared between cloned instances of Bitmap. So I performed some tests to find out the differences between Bitmap.Clone()and new Bitmap().

阅读以前的答案,我担心像素数据会在位图的克隆实例之间共享。所以我进行了一些测试以找出Bitmap.Clone()和之间的差异new Bitmap()

Bitmap.Clone()keeps the original file locked:

Bitmap.Clone()保持原始文件锁定:

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

Using new Bitmap(original)instead will unlock the file after original.Dispose(), and the exception will not be thrown. Using the Graphicsclass to modify the clone (created with .Clone()) will not modify the original:

使用new Bitmap(original)改为将在 之后解锁文件original.Dispose(),并且不会抛出异常。使用Graphics类修改克隆(使用.Clone())不会修改原始:

  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

Similarly, using the LockBitsmethod yields different memory blocks for the original and clone:

同样,使用该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);

The results are the same with both object ICloneable.Clone()and Bitmap Bitmap.Clone(Rectangle, PixelFormat).

结果是既同object ICloneable.Clone()Bitmap Bitmap.Clone(Rectangle, PixelFormat)

Next, I tried some simple benchmarks using the following code.

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

Storing 50 copies in the list took 6.2 seconds and resulted in 1.7 GB memory usage (the original image is 24 bpp and 3456 x 2400 pixels = 25 MB):

在列表中存储 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));

Using Clone()instead I could store 1 000 000 copies in the list during 0.7 seconds and using 0.9 GB. As expected, Clone()is very light-weight in comparison to new Bitmap():

使用Clone(),而不是我可以存储在0.7秒列表中的1 000万份,并使用0.9 GB。正如预期的那样,Clone()与以下相比非常轻巧new Bitmap()

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

Clones using the Clone()method are copy-on-write. Here I change one random pixel to a random color on the clone. This operation seems to trigger a copy of all pixel data from the original, because we're now back at 7.8 seconds and 1.6 GB:

使用该Clone()方法的克隆是写时复制。在这里,我将克隆上的一个随机像素更改为随机颜色。此操作似乎会触发原始所有像素数据的副本,因为我们现在回到 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);
  }

Just creating a Graphicsobject from the image will not trigger the copy:

Graphics仅从图像创建对象不会触发复制:

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

You have to draw something using the Graphicsobject in order to trigger the copy. Finally, using LockBitson the other hand, will copy the data even if ImageLockMode.ReadOnlyis specified:

您必须使用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);
  }