c# - para - ¿Cómo uso mapas de bits grandes en.NET?
elementos necesarios para dibujar un pixel en c# (7)
Esta es una pregunta de dos partes. La primera pregunta es cómo puede cargar imágenes grandes sin quedarse sin memoria (1), la segunda está mejorando el rendimiento de carga (2).
(1) Concider una aplicación como Photoshop donde tiene la capacidad de trabajar con enormes imágenes consumiendo gigabites en el sistema de archivos. Mantener la imagen completa en la memoria y aún tener suficiente memoria libre para realizar operaciones (filtros, procesamiento de imágenes, etc., o simplemente agregar capas) sería imposible en la mayoría de los sistemas (incluso en sistemas de 8gb x64).
Es por eso que aplicaciones como esta usan el concepto de archivos de intercambio. Internamente, estoy asumiendo que photoshop usa un formato de archivo propietario, adecuado para el diseño de su aplicación y construido para admitir cargas parciales del intercambio, lo que les permite cargar partes de un archivo en la memoria para procesarlo.
(2) Performande se puede mejorar (bastante) escribiendo cargadores personalizados para cada formato de archivo. Esto requiere que lea los encabezados de los archivos y la estructura de los formatos de archivo con los que desea trabajar. Una vez que tienes la mano, no es tan malo, pero no es tan trivial como hacer una llamada a un método.
Por ejemplo, puede buscar en Google FastBitmap para ver ejemplos de cómo puede cargar un archivo de mapa de bits (BMP) muy rápido, incluido decodificar el encabezado de mapa de bits. Esto implicó pInvoke y para darte una idea sobre lo que enfrentas necesitarás definir las estructuras de bitmap como
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BITMAPFILEHEADER
{
public Int16 bfType;
public Int32 bfSize;
public Int16 bfReserved1;
public Int16 bfReserved2;
public Int32 bfOffBits;
}
[StructLayout(LayoutKind.Sequential)]
public struct BITMAPINFO
{
public BITMAPINFOHEADER bmiHeader;
public RGBQUAD bmiColors;
}
[StructLayout(LayoutKind.Sequential)]
public struct BITMAPINFOHEADER
{
public uint biSize;
public int biWidth;
public int biHeight;
public ushort biPlanes;
public ushort biBitCount;
public BitmapCompression biCompression;
public uint biSizeImage;
public int biXPelsPerMeter;
public int biYPelsPerMeter;
public uint biClrUsed;
public uint biClrImportant;
}
Posiblemente funcione con la creación de un DIB ( http://www.herdsoft.com/ti/davincie/imex3j8i.htm ) y rarezas como datos que se almacenan "al revés" en un mapa de bits que debe tener en cuenta o verá una imagen de espejo cuando la abres :-)
Ahora eso es solo para bitmaps. Digamos que querías hacer PNG, entonces necesitarías hacer cosas similares, pero decodificando el encabezado PNG, que en su forma más simple no es tan difícil, pero si quieres obtener soporte completo para la especificación PNG, entonces te espera un divertido paseo: - )
PNG es diferente para decir un mapa de bits ya que usa un formato basado en fragmento donde tiene "encabezados" que puede colocar para buscar los datos diferentes. Ejemplo de algunos trozos que utilicé mientras jugaba con el formato fue
string[] chunks =
new string[] {"?PNG", "IHDR","PLTE","IDAT","IEND","tRNS",
"cHRM","gAMA","iCCP","sBIT","sRGB","tEXt","zTXt","iTXt",
"bKGD","hIST","pHYs","sPLT","tIME"};
También tendrá que aprender acerca de las sumas de comprobación de Adler32 para los archivos PNG. Por lo tanto, cada formato de archivo que desee hacer agregará un conjunto diferente de desafíos.
Realmente me gustaría poder dar ejemplos de código fuente más completos en mi respuesta, pero es un tema complejo, y para ser sincero, no he implementado un intercambio yo mismo, así que no podría dar demasiados consejos sólidos sobre eso.
La respuesta corta es que las capacidades de procesamiento de imágenes en el BCL no son tan buenas. La respuesta mediana sería intentar y encontrar si alguien ha escrito una biblioteca de imágenes que podría ayudarlo y la respuesta más larga sería quitarse las mangas y escribir el núcleo de su aplicación usted mismo.
Como me conoces en la vida real, sabes dónde encontrarme;)
Intento escribir una aplicación de visualización de imágenes liviana. Sin embargo, hay limitaciones de memoria del sistema con .NET.
Cuando intento cargar mapas de bits grandes ( 9000 x 9000 px o más, 24 bits), obtengo una System.OutOfMemoryException. Esto es en una PC con Windows 2000 con 2 GB de RAM (de los cuales se agotaron 1.3 GB). También lleva mucho tiempo intentar cargar los archivos.
El siguiente código genera este error:
Image image = new Bitmap(filename);
using (Graphics gfx = this.CreateGraphics())
{
gfx.DrawImage(image, new Point(0, 0));
}
Como lo hace este código:
Stream stream = (Stream)File.OpenRead(filename);
Image image = Image.FromStream(stream, false, false);
using (Graphics gfx = this.CreateGraphics())
{
gfx.DrawImage(image, new Rectangle(0, 0, 100, 100), 4000, 4000, 100, 100, GraphicsUnit.Pixel);
}
Además, es suficiente hacer exactamente esto:
Bitmap bitmap = new Bitmap(filename);
IntPtr handle = bitmap.GetHbitmap();
El último código fue diseñado para ser usado con GDI. Mientras investigaba esto, descubrí que esto es de hecho un problema de memoria donde .NET intenta asignar el doble de lo que se necesita en un solo bloque de memoria.
http://bytes.com/groups/net-c/279493-drawing-large-bitmaps
Sé por otras aplicaciones (Internet Explorer, MS Paint, etc.) que ES POSIBLE abrir imágenes grandes, y bastante rápido. Mi pregunta es, ¿cómo uso grandes mapas de bits con .NET?
¿Hay alguna forma de transmitirlos o cargarlos sin memoria?
¿Puedes crear un nuevo mapa de bits en blanco con las mismas dimensiones y profundidad de color? Si ese es el caso, al menos sabe que su entorno puede manejar la imagen. Entonces, el problema reside en el subsistema de carga de imágenes, ya que, por supuesto, el enlace indicado podría ser el caso.
Supongo que podría escribir sus propios cargadores de mapas de bits, pero eso es mucho trabajo para formatos no triviales, así que no lo sugeriría.
Tal vez haya bibliotecas de reemplazo disponibles que solucionen estos problemas con los cargadores estándar.
Entonces, ¿está diciendo que no es la carga del mapa de bits sino la representación la que causa la falta de memoria?
Si es así, ¿puedes usar Bitmap.LockBits para obtener los píxeles y escribir un resizer básico de imagen tú mismo?
Qué pasa:
Image image = new Bitmap(filename);
using (Graphics gfx = Graphics.FromImage(image))
{
// Stuff
}
solo una solución rápida, tuve el mismo problema, creé una segunda instancia de mapa de bits y pasé el mapa de bits en el constructor.
Para una respuesta realmente completa, usaría Reflector para ver el código fuente de Paint.NET ( http://www.getpaint.net/ ); un programa avanzado de edición de gráficos escrito en C #.
(como se señala en el comentario, Paint.NET solía ser de código abierto, pero ahora está cerrado).
Una cosa simplemente me golpeó. ¿Estás dibujando la imagen completa y no solo la parte visible? No debe dibujar una porción más grande de la imagen de la que muestra en su aplicación, use los parámetros x, y, ancho y alto para restringir el área dibujada.