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