Al leer las respuestas anteriores, me preocupaba que los datos de píxeles se compartan entre las instancias clonadas de Bitmap. Entonces realicé algunas pruebas para descubrir las diferencias entre Bitmap.Clone()
y new Bitmap()
.
Bitmap.Clone()
mantiene bloqueado el archivo original:
Bitmap original = new Bitmap("Test.jpg");
Bitmap clone = (Bitmap) original.Clone();
original.Dispose();
File.Delete("Test.jpg"); // Will throw System.IO.IOException
Usando new Bitmap(original)
lugar se desbloqueará el archivo después de original.Dispose()
, y la excepción no será lanzada. Utilización de la clase Graphics
para modificar el clon (creado con .Clone()
) no modificará el original:
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
mismo modo, utilizando el método de LockBits
produce diferentes bloques de memoria para el original y el clon:
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);
El los resultados son los mismos con ambos object ICloneable.Clone()
y Bitmap Bitmap.Clone(Rectangle, PixelFormat)
.
A continuación, intenté algunos puntos de referencia simples utilizando el siguiente código.
Almacenamiento de 50 copias en la lista tomó 6.2 segundos y resultó en 1,7 uso de la memoria GB (la imagen original es 24 bpp y 3456 x 2400 píxeles = 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));
Usando Clone()
lugar que podría almacenar 1 000 000 copias en la lista durante 0.7 segundos y usando 0.9 GB. Como era de esperar, Clone()
es muy ligero en comparación con new Bitmap()
:
for(int i = 0; i < 1000000; i++)
{
list.Add((Bitmap) original.Clone());
}
clones usando el método Clone()
son copia en escritura. Aquí cambio un píxel aleatorio por un color aleatorio en el clon.Esta operación parece desencadenar una copia de todos los datos de los píxeles de la original, porque ahora estamos de vuelta en 7,8 segundos y 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);
}
Sólo la creación de un objeto Graphics
de la imagen no activará la copia:
for(int i = 0; i < 50; i++)
{
Bitmap clone = (Bitmap) original.Clone();
Graphics.FromImage(clone).Dispose();
list.Add(clone);
}
Tiene que dibujar algo con el objeto Graphics
para activar la copia. Por último, el uso de LockBits
por el contrario, va a copiar los datos, incluso si se especifica 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);
}
que tenía un caso donde el archivo que estaba leyendo era un archivo TIFF de 1 bit por píxel. 'nuevo mapa de bits (A)' devolvió un mapa de bits de 32 bits por píxel, mientras que '(mapa de bits) A.Clone()' todavía era de 1 bit por píxel. Como estaba incrustando la imagen en un PDF para enviarla posteriormente, mantener la imagen en 1 bit fue importante. @Aelios @ HansPassant – gmlobdell