2010-05-19 17 views
9

szukałem wokół dużo i tylko metody znalazłem tworzenia Texture2D z Bitmap to:Czy istnieje szybka alternatywa dla tworzenia Texture2D z obiektu Bitmap w 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); 
} 

i

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

Gdzie rgbValues ​​jest tablica bajtów zawierających bitmapy dane pikselowe w 32-bitowym formacie ARGB.

Moje pytanie brzmi, czy są jakieś szybsze podejścia, które mogę spróbować?

Piszę edytor map, który musi odczytywać obrazy w formacie niestandardowym (kafelki mapy) i konwertować je w tekstury Texture2D do wyświetlenia. Poprzednia wersja edytora, która była implementacją C++, przekształciła obrazy najpierw w bitmapy, a następnie w tekstury, które zostaną narysowane przy użyciu DirectX. Próbowałem zastosować to samo podejście, jednak oba powyższe podejścia są znacznie zbyt powolne. Aby załadować do pamięci wszystkie tekstury wymagane dla mapy wymagają pierwszego podejścia ~ 250 sekund i dla drugiego podejścia ~ 110 sekund na rozsądnym komputerze spec (dla porównania kod C++ trwa około 5 sekund). Jeśli istnieje metoda edycji danych tekstury bezpośrednio (np. W metodzie LockBits klasy Bitmap) to byłbym w stanie przekonwertować obrazy w formacie niestandardowym prosto na Texture2D i mam nadzieję, że zaoszczędzę czas przetwarzania.

Każda pomoc będzie bardzo ceniona.

Dzięki

Odpowiedz

9

Chcesz lockbitów? Otrzymujesz LockBits.

W mojej implementacji przekazałem w GraphicsDevice od osoby dzwoniącej, dzięki czemu mogłem uczynić tę metodę ogólną i statyczną.

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

Czy nie powinno to być "od osoby dzwoniącej", a nie "odbiór"? Z celów użytkownika tego kodu jest to ta funkcja, która jest adresatem. – drxzcl

+0

Dziękuję za odpowiedź, jednak miałem na myśli w pierwotnym pytaniu, że próbowałem tego podejścia - za pomocą SetData(). Przebiegłem dalszą analizę, a do stworzenia bitmap za pomocą LockBits potrzebowałem 2,7 sekundy. Kiedy dodałem do tego kodu, dla każdej wygenerowanej bitmapy: Texture2D tx = new Texture2D (urządzenie, bmp.Width, bmp.Height, 0, TextureUsage.None, SurfaceFormat.Color); Zwiększono czas potrzebny do 74 sekund !! Wystarczy utworzyć pustą teksturę dla każdej bitmapy, a nawet jej nie wypełniać. Ten rodzaj czasu ładowania jest niedopuszczalny w przypadku gier 2D. Nie sądziłem, że XNA będzie tak powolny. :/ –

+0

@Ranieri, dzięki - naprawione! – bufferz

3

znalazłem musiałem podać PixelFormat jako .Format32bppArgb podczas korzystania LockBits jak sugerujesz, aby pobrać obrazy kamery.

 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

uległy one zmianie formatu z bgra do RGBA w XNA 4.0, tak że metoda daje dziwne kolory, czerwone i niebieskie kanały musi być włączony. Oto metoda, którą napisałem, że jest superszybka! (ładuje 1500x 256x256 pikseli tekstur w około 3 sekundy).

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

Nice! Ale czy nie powinieneś modyfikować danych bitmapowych, odkąd zablokowałeś je w ReadOnly? Możesz go najpierw skopiować, a następnie dokonać konwersji. Lub jeszcze prostsze, po prostu wykonaj konwersję i kopiuj w jednym kroku! (Przypisz imgData [i] zamiast byteData [i], wtedy nie potrzebujesz wcale kopii!) – Cameron

+0

O, dzięki, to działa dla mnie. –

+1

+1 Jest to przydatne, chociaż (jak sugerował @Cameron) byłoby lepiej, gdybyś nie zmienił oryginalnej bitmapy, ale zamiast tego pracował bezpośrednio na tablicy 'imgData' (nie musiałbyś nawet używać' unsafe' w tym wypadku). – Groo

1

Kiedy pierwszy raz przeczytałem to pytanie, Przypuszczałem, że to SetData wydajność to była granica. Jednak czytając komentarze OP w górnej odpowiedzi, wydaje się przydzielać lot z duży Texture2D.

Jako alternatywę rozważ użycie puli tekstur2, które można przeznaczyć w razie potrzeby, wrócić do puli, gdy nie jest już potrzebna.

Po raz pierwszy każdy plik tekstury jest potrzebny (lub w "wstępnym załadowaniu" na początku procesu, w zależności od tego, gdzie ma nastąpić opóźnienie), załaduj każdy plik do tablicy byte[]. (Przechowuj te tablice byte[] w pamięci podręcznej LRU - chyba że masz pewność, że masz wystarczającą ilość pamięci, aby utrzymać je wszystkie przez cały czas.) Jeśli potrzebujesz jednej z tych tekstur, chwyć jedną z tekstur puli, (przydzielając nową jeden, jeśli żaden z odpowiednich rozmiarów nie jest dostępny), SetData z twojej tablicy bajtów - altówka, masz teksturę.

[Pominąłem ważne szczegóły, takie jak potrzeba tekstury, która ma być powiązana z konkretnym urządzeniem - ale możesz określić wszelkie potrzeby od parametrów do metod, do których dzwonisz. Chodzi mi o to, aby zminimalizować liczbę połączeń z konstruktorem Texture2D, zwłaszcza jeśli masz dużo dużych tekstur.]

Jeśli masz ochotę i masz do czynienia z teksturami o różnych rozmiarach, możesz również zastosować LRU Pamięć podręczna zasady do puli. W szczególności śledź całkowitą liczbę bajtów "wolnych" obiektów przechowywanych w puli. Jeśli ta suma przekroczy określony próg (może być połączony z całkowitą liczbą "darmowych" obiektów), to przy następnym żądaniu wyrzuć najstarsze darmowe przedmioty z puli (o niewłaściwym rozmiarze lub innych złych parametrach), aby pozostać poniżej dozwolonego limitu próg "zmarnowanej" pamięci podręcznej.

BTW, możesz zrobić dobrze po prostu śledząc próg i wyrzucając wszystkie wolnych obiektów po przekroczeniu progu. Minusem jest chwilowa czka, gdy następnym razem przydzielimy kilka nowych tekstur - które można poprawić, jeśli masz informacje na temat rozmiarów, które powinieneś zachować. Jeśli to nie wystarczy, potrzebujesz LRU.

+0

Mimo że minęło 8 lat, doceniam, że napisałeś odpowiedź na moje pytanie. Myślę, że twoje podejście może rzeczywiście dobrze działać, jeśli wezwanie do konstruktora Texture2D jest wąskim gardłem. –