据我所知,有两种方法可以复制位图.
Bitmap.Clone()
Bitmap A = new Bitmap("somefile.png");
Bitmap B = (Bitmap)A.Clone();
Run Code Online (Sandbox Code Playgroud)
新的位图()
Bitmap A = new Bitmap("somefile.png");
Bitmap B = new Bitmap(A);
Run Code Online (Sandbox Code Playgroud)
这些方法有何不同?我对内存和线程方面的差异特别感兴趣.
Anl*_*nlo 106
阅读之前的答案,我担心像素数据将在克隆的Bitmap实例之间共享.所以我进行了一些测试,以找出Bitmap.Clone()
和之间的差异new Bitmap()
.
Bitmap.Clone()
保持原始文件锁定:
Bitmap original = new Bitmap("Test.jpg");
Bitmap clone = (Bitmap) original.Clone();
original.Dispose();
File.Delete("Test.jpg"); // Will throw System.IO.IOException
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
同样,使用该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);
Run Code Online (Sandbox Code Playgroud)
结果是既同object ICloneable.Clone()
和Bitmap Bitmap.Clone(Rectangle, PixelFormat)
.
接下来,我使用以下代码尝试了一些简单的基准测试.
在列表中存储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));
Run Code Online (Sandbox Code Playgroud)
使用Clone()
,而不是我可以存储在0.7秒列表中的1 000万份,并使用0.9 GB.正如所料,Clone()
与以下相比,重量非常轻new Bitmap()
:
for(int i = 0; i < 1000000; i++)
{
list.Add((Bitmap) original.Clone());
}
Run Code Online (Sandbox Code Playgroud)
使用该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);
}
Run Code Online (Sandbox Code Playgroud)
Graphics
仅从图像创建对象不会触发副本:
for(int i = 0; i < 50; i++)
{
Bitmap clone = (Bitmap) original.Clone();
Graphics.FromImage(clone).Dispose();
list.Add(clone);
}
Run Code Online (Sandbox Code Playgroud)
您必须使用该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);
}
Run Code Online (Sandbox Code Playgroud)
Han*_*ant 69
这是"深"和"浅"副本之间的共同区别,也是几乎不赞成的IClonable接口的问题.Clone()方法创建一个新的Bitmap对象,但像素数据与原始位图对象共享.Bitmap(Image)构造函数还会创建一个新的Bitmap对象,但该对象具有自己的像素数据副本.
使用克隆()是非常罕见有用.关于它的很多问题,程序员希望Clone()避免使用位图的典型问题,锁定加载它的文件.它没有.当您传递对处理位图的代码的引用并且您不想丢失对象时,仅使用Clone().
归档时间: |
|
查看次数: |
44185 次 |
最近记录: |