examples - image to bitmap c#
¿Es Graphics.DrawImage demasiado lento para imágenes más grandes? (3)
GDI + no es un demonio de la velocidad de ninguna manera. Cualquier manipulación seria de la imagen por lo general tiene que ir al lado nativo de las cosas (pInvoke llamadas y / o manipulación a través de un puntero obtenido al llamar a LockBits
).
¿Has mirado en XNA / DirectX / OpenGL? Estos son marcos diseñados para el desarrollo de juegos y serán órdenes de magnitud más eficientes y flexibles que el uso de un marco de interfaz de usuario como WinForms o WPF. Todas las bibliotecas ofrecen enlaces C #.
Podría invocar en código nativo, utilizando funciones como BitBlt , pero también existe una sobrecarga asociada con el cruce del límite del código administrado.
Actualmente estoy trabajando en un juego y deseo tener un menú principal con imagen de fondo.
Sin embargo, me parece que el método Graphics.DrawImage()
realmente lento. He hecho alguna medida. Supongamos que MenuBackground es mi imagen de recurso con una resolución de 800 x 1200 píxeles. Lo dibujaré en otro mapa de bits de 800 x 1200 (primero renderizo todo a un mapa de bits del búfer, luego lo escalo y finalmente lo dibujo en la pantalla, así es como manejo la posibilidad de que haya varias resoluciones de jugadores. Pero no debería afectar De cualquier manera, ver el siguiente párrafo).
Así que he medido el siguiente código:
Stopwatch SW = new Stopwatch();
SW.Start();
// First let''s render background image into original-sized bitmap:
OriginalRenderGraphics.DrawImage(Properties.Resources.MenuBackground,
new Rectangle(0, 0, Globals.OriginalScreenWidth, Globals.OriginalScreenHeight));
SW.Stop();
System.Windows.Forms.MessageBox.Show(SW.ElapsedMilliseconds + " milliseconds");
El resultado es bastante sorprendente para mí: el Stopwatch
mide algo entre 40 - 50 milliseconds
. Y como la imagen de fondo no es lo único que se puede dibujar, el menú completo tarda más de 100 ms en visualizarse, lo que implica un retraso observable.
He intentado dibujarlo en el objeto Graphics dado por el evento Paint, pero el resultado fue de 30 - 40 milliseconds
, no ha cambiado mucho.
Entonces, ¿significa que Graphics.DrawImage()
es inutilizable para dibujar imágenes más grandes? Si es así, ¿qué debo hacer para mejorar el rendimiento de mi juego?
GDI + probablemente no sea la mejor opción para los juegos. DirectX / XNA o OpenGL deben ser preferidos ya que utilizan cualquier aceleración de gráficos posible y son muy rápidos.
Sí, es demasiado lento.
Me encontré con este problema hace varios años al desarrollar Paint.NET (desde el principio, en realidad, ¡y fue bastante frustrante!). El rendimiento de la representación fue pésimo, ya que siempre fue proporcional al tamaño del mapa de bits y no al tamaño del área que se le pidió que redibujara. Es decir, la tasa de cuadros se redujo a medida que aumentaba el tamaño del mapa de bits, y la tasa de cuadros nunca subía, ya que el tamaño del área inválida / redibujada disminuía al implementar OnPaint () y llamar a Graphics.DrawImage (). Un pequeño mapa de bits, digamos 800x600, siempre funcionó bien, pero las imágenes más grandes (por ejemplo, 2400x1800) eran muy lentas. (Puede suponer, de todos modos, en el párrafo anterior, que no sucedió nada adicional, como escalar con un filtro Bicubic costoso, lo que habría afectado negativamente al rendimiento).
Es posible forzar a WinForms a usar GDI en lugar de GDI + y evitar incluso la creación de un objeto de Graphics
detrás de la escena, en cuyo punto puede colocar otro kit de herramientas de procesamiento (p. Ej., Direct2D). Sin embargo, no es simple. Hago esto en Paint.NET, y puedes ver lo que se requiere al usar algo como Reflector en la clase llamada GdiPaintControl
en el DLL SystemLayer, pero para lo que estás haciendo, lo considero un último recurso.
Sin embargo, el tamaño de mapa de bits que está utilizando (800x1200) aún debería funcionar lo suficientemente bien en GDI + sin tener que recurrir a interoperabilidad avanzada, a menos que esté apuntando a un Pentium II de 300MHz. Aquí hay algunos consejos que podrían ayudar:
- Si está utilizando un mapa de bits opaco (sin alfa / transparencia) en la llamada a
Graphics.DrawImage()
, y especialmente si es un mapa de bits de 32 bits con un canal alfa (pero sabe que es opaco o no le importa) , luego configureGraphics.CompositingMode
enCompositingMode.SourceCopy
antes de llamar aDrawImage()
(asegúrese de volver a establecerlo en el valor original, de lo contrario, las primitivas de dibujo normales se verán muy feas). Esto omite una gran cantidad de matematicas de mezcla extra por píxel. - Asegúrese de que
Graphics.InterpolationMode
no esté configurado en algo comoInterpolationMode.HighQualityBicubic
. UsarNearestNeighbor
será el más rápido, aunque si hay algún estiramiento puede que no se vea muy bien (a menos que se estire exactamente 2x, 3x, 4x, etc.) Por lo general,Bilinear
es un buen compromiso. Nunca debe usar nada que no seaNearestNeighbor
si el tamaño del mapa de bits coincide con el área que está dibujando, en píxeles. - Dibuje siempre en el objeto
Graphics
que se le entregó enOnPaint()
. - Siempre haz tu dibujo en
OnPaint
. Si necesita volver a dibujar un área, llame aInvalidate()
. Si necesita que el dibujo suceda ahora, llame aUpdate()
después deInvalidate()
. Este es un enfoque razonable, ya que los mensajes WM_PAINT (que dan como resultado una llamada aOnPaint()
) son mensajes de "baja prioridad". Cualquier otro procesamiento por parte del administrador de ventanas se realizará primero, y por lo tanto, podría terminar con un montón de saltos de cuadros y enganches de lo contrario. - Usar un
System.Windows.Forms.Timer
como un temporizador de framerate / tick no funcionará muy bien. Estos se implementan utilizandoSetTimer
de Win32 y dan como resultado mensajes WM_TIMER que luego provocan el eventoTimer.Tick
, y WM_TIMER es otro mensaje de baja prioridad que se envía solo cuando la cola de mensajes está vacía. Es mejor usarSystem.Threading.Timer
y luego usarControl.Invoke()
(para asegurarte de que estás en el hilo correcto) y llamar aControl.Update()
. - En general, no utilice
Control.CreateGraphics()
. (corolario a ''dibujar siempre enOnPaint()
'' y ''usar siempre losGraphics
que te ofreceOnPaint()
'') - Recomiendo no usar el controlador de eventos Paint. En su lugar, implemente
OnPaint()
en la clase que está escribiendo, que debe derivarse deControl
. Derivar de otra clase, por ejemplo,PictureBox
oUserControl
, no agregará ningún valor para usted o agregará una sobrecarga adicional. (Por lo general, laPictureBox
BTW se malinterpreta. Es probable que casi nunca quieras usarlo).
Espero que ayude.