Đọc các câu trả lời trước, tôi lo lắng rằng dữ liệu pixel sẽ được chia sẻ giữa các phiên bản nhân bản của Bitmap. Vì vậy, tôi đã thực hiện một số thử nghiệm để tìm ra sự khác biệt giữa Bitmap.Clone()
và new Bitmap()
.
Bitmap.Clone()
giữ file gốc khóa:
Bitmap original = new Bitmap("Test.jpg");
Bitmap clone = (Bitmap) original.Clone();
original.Dispose();
File.Delete("Test.jpg"); // Will throw System.IO.IOException
Sử dụng new Bitmap(original)
thay vào đó sẽ mở khóa các tập tin sau khi original.Dispose()
, và ngoại lệ sẽ không được ném. Sử dụng lớp Graphics
để sửa đổi các bản sao (được tạo ra với .Clone()
) sẽ không sửa đổi bản gốc:
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
Tương tự như vậy, sử dụng phương pháp LockBits
mang lại khối bộ nhớ khác nhau cho các bản sao gốc và:
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);
Các kết quả giống nhau với cả hai số object ICloneable.Clone()
và Bitmap Bitmap.Clone(Rectangle, PixelFormat)
.
Tiếp theo, tôi đã thử một số điểm chuẩn đơn giản bằng cách sử dụng mã sau đây.
Lưu 50 bản sao trong danh sách mất 6.2 giây và kết quả là 1,7 sử dụng bộ nhớ GB (ảnh gốc là 24 bpp và 3456 x 2400 pixel = 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));
Sử dụng Clone()
thay vào đó tôi có thể lưu trữ 1 000 000 bản trong danh sách trong vòng 0,7 giây và sử dụng 0,9 GB. Đúng như dự đoán, Clone()
là rất nhẹ so với new Bitmap()
:
for(int i = 0; i < 1000000; i++)
{
list.Add((Bitmap) original.Clone());
}
nhái sử dụng phương pháp Clone()
là copy-on-write. Ở đây tôi thay đổi một pixel ngẫu nhiên thành màu ngẫu nhiên trên bản sao.Thao tác này dường như kích hoạt một bản sao của tất cả các dữ liệu pixel từ bản gốc, bởi vì chúng tôi bây giờ trở lại ở 7,8 giây và 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);
}
Chỉ cần tạo một đối tượng Graphics
từ hình ảnh sẽ không kích hoạt các bản sao:
for(int i = 0; i < 50; i++)
{
Bitmap clone = (Bitmap) original.Clone();
Graphics.FromImage(clone).Dispose();
list.Add(clone);
}
Bạn phải vẽ thứ gì đó bằng cách sử dụng đối tượng Graphics
để kích hoạt bản sao. Cuối cùng, sử dụng LockBits
mặt khác, sẽ sao chép các dữ liệu ngay cả khi ImageLockMode.ReadOnly
được quy định:
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);
}
Tôi đã có một trường hợp hồ sơ tôi đã đọc là 1 bit trên mỗi điểm ảnh tập tin TIFF. 'bitmap mới (A)' trả về bitmap 32 bit cho mỗi pixel, trong khi '(Bitmap) A.Clone()' vẫn là 1 bit trên mỗi pixel. Vì tôi đã nhúng hình ảnh vào một tệp PDF để gửi email sau này, việc giữ hình ảnh ở mức 1 bit là quan trọng. @Aelios @HansPassant – gmlobdell