2012-10-03 9 views
51

Per quanto ne so, ci sono due modi per copiare una bitmap.Qual è la differenza tra Bitmap.Clone() e la nuova Bitmap (Bitmap)?

Bitmap.Clone()

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

nuova Bitmap()

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

Come questi approcci differiscono? Sono particolarmente interessato alla differenza in termini di memoria e threading.

+3

ho avuto un caso in cui il file che stavo leggendo era un file TIFF 1 bit per pixel. 'new Bitmap (A)' ha restituito una bitmap a 32 bit per pixel, mentre '(Bitmap) A.Clone()' era ancora 1 bit per pixel. Poiché stavo incorporando l'immagine in un PDF per l'invio successivo via email, mantenere l'immagine a 1 bit era importante. @Aelios @HansPassant – gmlobdell

risposta

49

È la differenza comune tra una copia "profonda" e una "superficiale", anche un problema con l'interfaccia IClonable quasi deprecata. Il metodo Clone() crea un nuovo oggetto Bitmap ma i dati del pixel sono condivisi con l'oggetto bitmap originale. Il costruttore Bitmap (Image) crea anche un nuovo oggetto Bitmap ma uno che ha la propria copia dei dati dei pixel.

Utilizzo di Clone() è molto raramente utile. Un sacco di domande a riguardo su SO dove il programmatore spera che Clone() eviti il ​​tipico problema con le bitmap, il blocco sul file dal quale è stato caricato. Non è così. Utilizzare solo Clone() quando si passa un riferimento al codice che dispone la bitmap e non si desidera perdere l'oggetto.

+4

concordato. Abbiamo usato Clone() nel caso in cui abbiamo bisogno di usare lo stesso Bitmap usato (non modificato) in molti punti, ma volevamo ridurre la quantità di memoria utilizzata dalle copie. Una cosa che non so è se si modifica uno dei cloni (ad es. SetPixel), se questo causa la modifica di tutti i dati di pixel condivisi, o se fa sì che quello modificato assegni i propri dati di pixel (modificando quindi solo il proprio). –

+0

@MattSmith, i dati verranno copiati dopo il comando di blocco, anche con il flag ReandOnly. – Pedro77

+0

@HansPassant, Per * "dispone" *, vuoi dire, * "chiama il metodo' .Dispose() '" quando dici questo: * "Usa solo Clone() quando passi un riferimento al codice che dispone il bitmap e non si vuole perdere l'oggetto. "* – kdbanman

87

Leggendo le risposte precedenti, mi sono preoccupato che i dati dei pixel sarebbero stati condivisi tra le istanze di Bitmap clonate. Quindi ho eseguito alcuni test per scoprire le differenze tra Bitmap.Clone() e new Bitmap().

Bitmap.Clone() mantiene il file originale bloccato:

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

Utilizzando new Bitmap(original) invece sarà sbloccare il file dopo original.Dispose(), e l'eccezione non saranno gettati. Uso della classe Graphics modificare il clone (creato con .Clone()) non modificherà dell'originale:

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 

Analogamente, utilizzando il metodo LockBits produce diversi blocchi di memoria per il clone originale e:

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

L' i risultati sono gli stessi sia per object ICloneable.Clone() sia per Bitmap Bitmap.Clone(Rectangle, PixelFormat).

Successivamente, ho provato alcuni semplici benchmark utilizzando il seguente codice.

Memorizzazione di 50 copie della lista ha preso 6,2 secondi e ha portato in 1.7 utilizzo della memoria GB (l'immagine originale è 24 BPP e 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)); 

Utilizzando Clone() invece ho potuto memorizzare 1 000 000 copie nell'elenco durante 0,7 secondi e utilizzando 0,9 GB. Come previsto, Clone() è molto leggero rispetto a new Bitmap():

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

cloni utilizzando il metodo Clone() sono copy-on-write. Qui cambio un pixel casuale in un colore casuale sul clone.Questa operazione sembra innescare una copia di tutti i dati dei pixel rispetto all'originale, perché siamo ora indietro a 7,8 secondi e 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); 
    } 

solo la creazione di un oggetto Graphics dall'immagine non attivano la copia:

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

È necessario disegnare qualcosa utilizzando l'oggetto Graphics per attivare la copia. Infine, utilizzando LockBits d'altra parte, copierà i dati anche se viene specificato 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); 
    } 
+5

Post man molto bello! – Pedro77

+1

Quindi, qual è il metodo migliore per ottenere una copia completamente separata dell'immagine e tutti i dati? – Don

+0

Se hai bisogno di una copia separata, vorrei usare la nuova Bitmap(). Ciò non manterrà il blocco dei file sul file originale e la volta e la memoria della cpu saranno utilizzati nel luogo di copia, non nel punto in cui si inizia a modificare la copia. Ma se non sei sicuro se la copia verrà modificata o meno, .Clone() è probabilmente una scelta migliore. – Anlo

Problemi correlati