2010-05-19 14 views
9

He mirado alrededor de un lote y los únicos métodos que he encontrado para la creación de un Texture2D de un mapa de bits son:¿Existe una alternativa rápida a la creación de un Texture2D a partir de un objeto Bitmap en XNA?

using (MemoryStream s = new MemoryStream()) 
{ 
    bmp.Save(s, System.Drawing.Imaging.ImageFormat.Png); 
    s.Seek(0, SeekOrigin.Begin); 
    Texture2D tx = Texture2D.FromFile(device, s); 
} 

y

Texture2D tx = new Texture2D(device, bmp.Width, bmp.Height, 
         0, TextureUsage.None, SurfaceFormat.Color); 
tx.SetData<byte>(rgbValues, 0, rgbValues.Length, SetDataOptions.NoOverwrite); 

Dónde rgbValues ​​es una matriz de bytes que contiene la década de mapa de bits datos de píxeles en formato ARGB de 32 bits.

Mi pregunta es, ¿hay algún enfoque más rápido que pueda probar?

Estoy escribiendo un editor de mapas que tiene que leer en imágenes de formato personalizado (mosaicos de mapas) y convertirlos en texturas Texture2D para mostrar. La versión anterior del editor, que era una implementación de C++, convertía las imágenes primero en mapas de bits y luego en texturas que se dibujaban con DirectX. He intentado el mismo enfoque aquí, sin embargo, los dos enfoques anteriores son demasiado lentos. Para cargar en la memoria, todas las texturas requeridas para un mapa tardan aproximadamente 250 segundos para la primera aproximación y para la segunda aproximación aproximadamente 110 segundos en una computadora con especificaciones razonables (para comparación, el código C++ tardó aproximadamente 5 segundos). Si hay un método para editar los datos de una textura directamente (como con el método LockBits de la clase Bitmap), entonces podría convertir las imágenes de formato personalizado directamente en Texture2D y, con suerte, ahorrar tiempo de procesamiento.

Cualquier ayuda sería muy apreciada.

Gracias

Respuesta

9

¿Quiere LockBits? Obtienes LockBits.

En mi implementación pasé en el GraphicsDevice de la persona que llama para poder hacer este método genérico y estático.

public static Texture2D GetTexture2DFromBitmap(GraphicsDevice device, Bitmap bitmap) 
{ 
    Texture2D tex = new Texture2D(device, bitmap.Width, bitmap.Height, 1, TextureUsage.None, SurfaceFormat.Color); 

    BitmapData data = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bitmap.PixelFormat); 

    int bufferSize = data.Height * data.Stride; 

    //create data buffer 
    byte[] bytes = new byte[bufferSize];  

    // copy bitmap data into buffer 
    Marshal.Copy(data.Scan0, bytes, 0, bytes.Length); 

    // copy our buffer to the texture 
    tex.SetData(bytes); 

    // unlock the bitmap data 
    bitmap.UnlockBits(data); 

    return tex; 
} 
+1

¿No debería ser "de la persona que llama" en lugar de "llamar"? A los fines del usuario de este código, es esta función la que llama el destinatario. – drxzcl

+0

Gracias por su respuesta, sin embargo, en mi pregunta original quise decir que había intentado este enfoque: usar SetData(). Ejecuté algunos análisis adicionales, y para crear los mapas de bits usando LockBits tomó 2.7 segundos. Cuando agregué a ese código, para cada Bitmap generado: Texture2D tx = new Texture2D (dispositivo, bmp.Width, bmp.Height, 0, TextureUsage.None, SurfaceFormat.Color); ¡Aumentó el tiempo requerido a 74 segundos! Solo para crear una textura en blanco para cada mapa de bits, y ni siquiera llenarlo. Este tipo de tiempo de carga no es aceptable para un juego 2D. No creía que XNA fuera tan lento. :/ –

+0

@Ranieri, gracias - ¡corregido! – bufferz

3

me encontré con que tenía que especificar el PixelFormat como .Format32bppArgb cuando se utiliza LockBits como usted sugiere para tomar imágenes de la webcam.

 BitmapData bmd = bmp.LockBits(new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), 
      System.Drawing.Imaging.ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); 
     int bufferSize = bmd.Height * bmd.Stride; 
     //create data buffer 
     byte[] bytes = new byte[bufferSize]; 
     // copy bitmap data into buffer 
     Marshal.Copy(bmd.Scan0, bytes, 0, bytes.Length); 

     // copy our buffer to the texture 
     Texture2D t2d = new Texture2D(_graphics.GraphicsDevice, bmp.Width, bmp.Height, 1, TextureUsage.None, SurfaceFormat.Color); 
     t2d.SetData<byte>(bytes); 
     // unlock the bitmap data 
     bmp.UnlockBits(bmd); 
     return t2d; 
7

que han cambiado el formato de BGRA a RGBA en XNA 4.0, por lo que el método da colores extraños, los canales rojo y azul necesita ser cambiado. ¡Este es un método que escribí que es súper rápido! (carga texturas de 1500x 256x256 píxeles en aproximadamente 3 segundos).

private Texture2D GetTexture(GraphicsDevice dev, System.Drawing.Bitmap bmp) 
    { 
     int[] imgData = new int[bmp.Width * bmp.Height]; 
     Texture2D texture = new Texture2D(dev, bmp.Width, bmp.Height); 

     unsafe 
     { 
      // lock bitmap 
      System.Drawing.Imaging.BitmapData origdata = 
       bmp.LockBits(new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat); 

      uint* byteData = (uint*)origdata.Scan0; 

      // Switch bgra -> rgba 
      for (int i = 0; i < imgData.Length; i++) 
      { 
       byteData[i] = (byteData[i] & 0x000000ff) << 16 | (byteData[i] & 0x0000FF00) | (byteData[i] & 0x00FF0000) >> 16 | (byteData[i] & 0xFF000000);       
      }     

      // copy data 
      System.Runtime.InteropServices.Marshal.Copy(origdata.Scan0, imgData, 0, bmp.Width * bmp.Height); 

      byteData = null; 

      // unlock bitmap 
      bmp.UnlockBits(origdata); 
     } 

     texture.SetData(imgData); 

     return texture; 
    } 
+2

¡Agradable! Pero, ¿no debería estar modificando los datos del mapa de bits desde que lo bloqueó con ReadOnly? Podrías copiarlo primero y luego hacer la conversión. ¡O incluso más simple, solo haz la conversión y copia en un solo paso! (Asignar a imgData [i] en lugar de byteData [i], ¡entonces no necesita la copia!) – Cameron

+0

Gracias, funciona para mí. –

+1

+1 Esto es práctico, aunque (como sugirió @Cameron) sería mejor si no cambiaras el mapa de bits original, sino que trabajaras en el arreglo 'imgData' directamente (ni siquiera tendrías que usar' inseguro' en ese caso). – Groo

1

Cuando leí por primera vez esta pregunta, supuso que era SetData rendimiento que era el límite. Sin embargo, al leer los comentarios de OP en la respuesta principal, parece estar asignando un lote de de grande Texture2D.

Como alternativa, considere tener un grupo de Texture2D, que asigne según sea necesario, regrese al grupo cuando ya no sea necesario.

La primera vez que se necesita cada archivo de textura (o en una "carga previa" al inicio del proceso, dependiendo de dónde desee la demora), cargue cada archivo en una matriz byte[]. (Almacene esas matrices byte[] en LRU Cache - a menos que esté seguro de tener suficiente memoria para mantenerlas todo el tiempo.) Luego, cuando necesite una de esas texturas, tome una de las texturas de la agrupación (asignando una nueva uno, si ninguno de los tamaños apropiados está disponible), SetData de su matriz de bytes - viola, tiene una textura.

[He omitido detalles importantes, como la necesidad de asociar una textura con un dispositivo específico, pero puede determinar cualquier necesidad desde los parámetros hasta los métodos que está llamando. El punto que estoy haciendo es minimizar las llamadas al constructor Texture2D, especialmente si tienes muchas texturas grandes.]

Si te gustan las texturas de diferentes tamaños, también puedes aplicar LRU Caché principios para el grupo. Específicamente, rastree la cantidad total de bytes de objetos "libres" que se encuentran en su grupo. Si ese total excede algún umbral que establezca (tal vez combinado con el número total de objetos "gratuitos"), en la próxima solicitud, deseche los elementos libres más antiguos del grupo (del tamaño incorrecto u otros parámetros incorrectos) para mantenerse por debajo del límite permitido. umbral del espacio de caché "desperdiciado".

Por cierto, puede hacer bien solo el seguimiento del umbral, y tirando todos los objetos libres cuando se supera el umbral. La desventaja es un contratiempo momentáneo la próxima vez que asigne un conjunto de nuevas texturas, que podrá mejorar si tiene información sobre los tamaños que debe mantener. Si eso no es lo suficientemente bueno, entonces necesita LRU.

+0

Aunque han pasado 8 años, le agradezco que haya escrito una respuesta a mi pregunta. Creo que su enfoque podría funcionar bien si la llamada al constructor Texture2D es el cuello de botella. –

Cuestiones relacionadas