2012-10-03 29 views
51

Hasta donde puedo decir, hay dos formas de copiar un mapa de bits.¿Cuál es la diferencia entre Bitmap.Clone() y el nuevo mapa de bits (mapa de bits)?

Bitmap.Clone()

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

nuevo mapa de bits()

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

¿En qué difieren estos enfoques? Estoy particularmente interesado en la diferencia en términos de memoria y enhebrado.

+3

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

Respuesta

49

Es la diferencia común entre una copia "profunda" y una "superficial", también un problema con la interfaz IClonable casi obsoleta. El método Clone() crea un nuevo objeto Bitmap pero los datos de píxeles se comparten con el objeto de mapa de bits original. El constructor Bitmap (Imagen) también crea un nuevo objeto Bitmap, pero uno que tiene su propia copia de los datos de píxeles.

El uso de Clone() es muy rara vez es útil. Muchas preguntas al respecto en SO, donde el programador espera que Clone() evite el problema típico con los mapas de bits, el bloqueo en el archivo desde el cual se cargó. No es así Solo use Clone() cuando pase una referencia al código que dispone el mapa de bits y no desea perder el objeto.

+4

De acuerdo. Usamos Clone() en el caso donde necesitamos usar tener el mismo mapa de bits utilizado (sin modificar) en muchos lugares, pero queríamos reducir la cantidad de memoria utilizada por las copias. Una cosa que no sé es si modifica uno de los clones (es decir, SetPixel), si eso hace que todos los datos de píxeles compartidos se modifiquen, o si hace que el modificado asigne sus propios datos de píxeles (por lo tanto, simplemente modificando su propio) –

+0

@MattSmith, los datos se copiarán después del comando de bloqueo, incluso con el indicador ReandOnly. – Pedro77

+0

@HansPassant, por * "descarta" *, ¿quieres decir? * "Llama al' .Disponer() 'método" * cuando dices esto: * "Usa solo Clonar() cuando pasas una referencia al código que elimina el mapa de bits y no desea perder el objeto. "* – kdbanman

87

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

¡Muy buen hombre! – Pedro77

+1

Entonces, ¿qué método es el mejor para obtener una copia completa de la imagen y todos los datos? – Don

+0

Si necesita una copia por separado, usaría un nuevo mapa de bits(). Esto no mantendrá el bloqueo de archivos en el archivo original y el tiempo de CPU y la memoria necesaria se usarán en el lugar de la copia, no en el lugar donde se comienza a modificar la copia. Pero si no está seguro de si la copia se modificará o no, .Clone() es probablemente una mejor opción. – Anlo

Cuestiones relacionadas