2012-10-03 20 views
51

Bildiğim kadarıyla, bir bit eşlem kopyalama iki yolu vardır.Bitmap.Clone() ve yeni Bitmap (Bitmap) arasındaki fark nedir? Söyleyebileceğim

Bitmap.Clone()

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

yeni Bitmap() bu yaklaşımlar farklılık nasıl

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

? Özellikle bellek ve iş parçacığı arasındaki farkla ilgileniyorum.

+3

ben okuyordum dosyası 1 bitlik piksel başına TIFF dosya olan bir dava vardı. '(Bitmap) A.Clone()', piksel başına 1 bit iken, "yeni Bitmap (A)", piksel bitmap başına 32 bit döndürdü. Resmi daha sonra e-postayla göndermek için PDF'ye yerleştirdiğimden, görüntüyü 1 bitde tutmak önemliydi. @Aelios @HansPassant – gmlobdell

cevap

49

Neredeyse-deprecated IClonable arayüzü ile de bir sorun, bir "derin" ve "sığ" kopya arasındaki ortak farktır. Clone() yöntemi yeni bir Bitmap nesnesi oluşturur, ancak piksel verileri orijinal bitmap nesnesiyle paylaşılır. Bitmap (Görüntü) yapıcısı da yeni bir Bitmap nesnesi oluşturur, ancak piksel verilerinin kendi kopyasına sahip olanıdır. Klon kullanma

() çok nadiren yararlıdır olduğunu. Programcının, Clone() 'ın bitmap'lerle tipik bir sorundan, yüklendiği dosyadaki kilitlemeden kaçınılacağını umuyoruz. Olmaz. Yalnızca, bitmap'i içeren kodlara bir başvuru ilettiğinizde ve nesneyi kaybetmek istemediğinizde Clone() kullanın. önceki cevapları Okuma

+4

Anlaşmalı. Clone() 'ı birçok yerde kullandığımız aynı Bitmap'e (değiştirilmemiş) sahip olduğumuz durumlarda kullandık, ancak kopyaların kullandığı bellek miktarını azaltmak istedik. Bilmediğim bir şey, eğer klonlardan birini (yani SetPixel) değiştirirseniz, eğer bu tüm paylaşılan piksel verilerinin değiştirilmesine neden oluyorsa veya değiştirilmiş olanın kendi piksel verilerini tahsis etmesine neden oluyorsa (böylece sadece kendi). –

+0

@MattSmith, veriler ReandOnly bayrağıyla bile kilit komutundan sonra kopyalanacaktır. – Pedro77

+0

@HansPassant, By * "dispose" *, demek istediniz, * ".Dispose()" yöntemini çağırır * yöntemini * söylerseniz: * "Yalnızca" Clone() kullanın; bitmap ve nesneyi kaybetmek istemezsiniz. "* – kdbanman

87

, ben piksel veri Bitmap klonlanmış örneği arasında paylaşılan olacağını endişelendim. Bu yüzden Bitmap.Clone() ve new Bitmap() arasındaki farkları bulmak için bazı testler yapılır.

Bitmap.Clone() kilitli orijinal dosyayı korur: original.Dispose() sonra dosyayı kilidini açacak yerine new Bitmap(original) kullanma

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

ve özel durum olmayacaktır. (.Clone() ile oluşturulan) klonunun modifiye edilmesi Graphics sınıfını kullanma orijinal değiştirmezler:

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:

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 

Benzer şekilde, LockBits yöntemi kullanılarak özgün ve klon için farklı bellek bloğu elde edilir sonuçlar object ICloneable.Clone() ve Bitmap Bitmap.Clone(Rectangle, PixelFormat) ikisi ile aynıdır.

Daha sonra, aşağıdaki kodu kullanarak bazı basit karşılaştırmaları denedim. listedeki 50 kopya saklama

6.2 saniye sürdü ve 1.7 GB bellek kullanımı (orijinal görüntü 24 bpp ve 3456 x 2400 piksel = 25 MB) ile sonuçlanmıştır:

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)); 

bunun yerine saklamak Clone() kullanılması Listede 0,7 saniyede 1 000 000 kopya ve 0,9 GB. Beklendiği gibi, Clone()new Bitmap() çok karşılaştırıldığında hafif geçerli: Clone() yöntemi kullanılarak

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

Klonlar kopya üzerinde yazma vardır. Burada bir rastgele pikseli klondaki rastgele bir renge değiştiriyorum.Sadece görüntüden bir Graphics nesnesi oluşturarak

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); 
    } 

kopyasını tetiklemez: Şimdi geri 7.8 saniye ve 1.6 GB çünkü bu işlem, orijinal tüm piksel verilerin bir kopyasını tetikler gözükmektedir

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

kopyayı tetiklemek için Graphics nesnesini kullanarak bir şey çizmek gerekir. Son olarak, diğer taraftan LockBits kullanarak ImageLockMode.ReadOnly belirtilmiş olsa bile verileri kopyalar:

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); 
    } 
+5

Çok güzel postacı! – Pedro77

+1

Peki, görüntünün ve tüm verilerin tam bir kopyasını almak için en iyi yöntem hangisidir? – Don

+0

Ayrı bir kopyasına ihtiyacınız varsa, yeni Bitmap() kullanırdım. Bu, orijinal dosyadaki dosya kilidini tutmaz ve gerekli olan cpu zamanı ve belleği, kopyayı değiştirmeye başladığınız yerde değil, kopya yerinde kullanılır. Ancak kopyanın değiştirilip değiştirilmeyeceğinden emin değilseniz, .Clone() muhtemelen daha iyi bir seçenektir. – Anlo