[MonoGame/XNA]Texture2D zu Bitmap

  • C#
  • .NET (FX) 4.5–4.8

Es gibt 8 Antworten in diesem Thema. Der letzte Beitrag () ist von ~blaze~.

    [MonoGame/XNA]Texture2D zu Bitmap

    Guten Abend,

    konventionell wird um ein Texture2D-Objekt als Bitmap zu repräsentieren ein MemoryStream generiert und in diesen das Texture2D-Objekt gespeichert und aus diesen
    dann ein Bitmap-Objekt generiert.

    Das ist jedoch perfomance-technisch sehr ungünstig.

    Gibt es eine schnellere und perfomantere Variante das zu realisieren?

    Lockbits ist unter anderem eine Möglichkeit; das Problem war jedoch, dass das von .GetData<byte> zurückgegebene Byte-array nicht verwendet werden konnte, um die Pixels in .UnlockBits zu aktualisieren.
    Kennt jemand ein Kompromiss?
    Und Gott alleine weiß alles am allerbesten und besser.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „φConst“ ()

    LockBitMap:

    C#-Quellcode

    1. public class LockBitmap
    2. {
    3. Bitmap source = null;
    4. IntPtr Iptr = IntPtr.Zero;
    5. BitmapData bitmapData = null;
    6. public byte[] Pixels { get; set; }
    7. public int Depth { get; private set; }
    8. public int Width { get; private set; }
    9. public int Height { get; private set; }
    10. public LockBitmap(Bitmap source)
    11. {
    12. this.source = source;
    13. }
    14. /// <summary>
    15. /// Lock bitmap data
    16. /// </summary>
    17. public void LockBits()
    18. {
    19. try
    20. {
    21. // Get width and height of bitmap
    22. Width = source.Width;
    23. Height = source.Height;
    24. // get total locked pixels count
    25. int PixelCount = Width * Height;
    26. // Create rectangle to lock
    27. Rectangle rect = new Rectangle(0, 0, Width, Height);
    28. // get source bitmap pixel format size
    29. Depth = System.Drawing.Bitmap.GetPixelFormatSize(source.PixelFormat);
    30. // Check if bpp (Bits Per Pixel) is 8, 24, or 32
    31. if (Depth != 8 && Depth != 24 && Depth != 32)
    32. {
    33. throw new ArgumentException("Only 8, 24 and 32 bpp images are supported.");
    34. }
    35. // Lock bitmap and return bitmap data
    36. bitmapData = source.LockBits(rect, ImageLockMode.ReadWrite,
    37. source.PixelFormat);
    38. // create byte array to copy pixel values
    39. int step = Depth / 8;
    40. Pixels = new byte[PixelCount * step];
    41. Iptr = bitmapData.Scan0;
    42. // Copy data from pointer to array
    43. Marshal.Copy(Iptr, Pixels, 0, Pixels.Length);
    44. }
    45. catch (Exception ex)
    46. {
    47. }
    48. }
    49. /// <summary>
    50. /// Unlock bitmap data
    51. /// </summary>
    52. public void UnlockBits()
    53. {
    54. try
    55. {
    56. // Copy data from byte array to pointer
    57. Marshal.Copy(Pixels, 0, Iptr, Pixels.Length);
    58. // Unlock bitmap data
    59. source.UnlockBits(bitmapData);
    60. }
    61. catch (Exception ex)
    62. {
    63. }
    64. }
    65. /// <summary>
    66. /// Get the color of the specified pixel
    67. /// </summary>
    68. /// <param name="x"></param>
    69. /// <param name="y"></param>
    70. /// <returns></returns>
    71. public Color GetPixel(int x, int y)
    72. {
    73. Color clr = Color.Empty;
    74. // Get color components count
    75. int cCount = Depth / 8;
    76. // Get start index of the specified pixel
    77. int i = ((y * Width) + x) * cCount;
    78. if (i > Pixels.Length - cCount)
    79. throw new IndexOutOfRangeException();
    80. if (Depth == 32) // For 32 bpp get Red, Green, Blue and Alpha
    81. {
    82. byte b = Pixels[i];
    83. byte g = Pixels[i + 1];
    84. byte r = Pixels[i + 2];
    85. byte a = Pixels[i + 3]; // a
    86. clr = Color.FromArgb(a, r, g, b);
    87. }
    88. if (Depth == 24) // For 24 bpp get Red, Green and Blue
    89. {
    90. byte b = Pixels[i];
    91. byte g = Pixels[i + 1];
    92. byte r = Pixels[i + 2];
    93. clr = Color.FromArgb(r, g, b);
    94. }
    95. if (Depth == 8)
    96. // For 8 bpp get color value (Red, Green and Blue values are the same)
    97. {
    98. byte c = Pixels[i];
    99. clr = Color.FromArgb(c, c, c);
    100. }
    101. return clr;
    102. }
    103. /// <summary>
    104. /// Set the color of the specified pixel
    105. /// </summary>
    106. /// <param name="x"></param>
    107. /// <param name="y"></param>
    108. /// <param name="color"></param>
    109. public void SetPixel(int x, int y, Color color)
    110. {
    111. // Get color components count
    112. int cCount = Depth / 8;
    113. // Get start index of the specified pixel
    114. int i = ((y * Width) + x) * cCount;
    115. if (Depth == 32) // For 32 bpp set Red, Green, Blue and Alpha
    116. {
    117. Pixels[i] = color.B;
    118. Pixels[i + 1] = color.G;
    119. Pixels[i + 2] = color.R;
    120. Pixels[i + 3] = color.A;
    121. }
    122. if (Depth == 24) // For 24 bpp set Red, Green and Blue
    123. {
    124. Pixels[i] = color.B;
    125. Pixels[i + 1] = color.G;
    126. Pixels[i + 2] = color.R;
    127. }
    128. if (Depth == 8)
    129. // For 8 bpp set color value (Red, Green and Blue values are the same)
    130. {
    131. Pixels[i] = color.B;
    132. }
    133. }
    134. }
    Ist aus codeproject...

    Konversion:

    C#-Quellcode

    1. pixelData = new byte[800 * 800];
    2. Texture.GetData<byte>(pixelData);
    3. Bitmap output = new Bitmap(800, 800);
    4. lockBits = new LockBitmap(output );
    5. lockBits.LockBits();
    6. lockBits.Pixels = pixelData;
    7. lockBits.UnlockBits();


    Resultat:
    Und Gott alleine weiß alles am allerbesten und besser.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „φConst“ ()

    du machst GetData als byte, dann aber nicht das gesamte bitmap?
    ich würde das außerdem selbst implementieren, wenns schon um geschwindigkeit geht, du hast hier nämlich unnötigen overhead.

    VB.NET-Quellcode

    1. int[] pixelData = new int[Texture.Width*Texture.Height];
    2. Texture.GetData<int>(pixelData);
    3. var bmp = new Bitmap(Texture.Width,Texture.Height);
    4. var bmpData = bmp.LockBits(new Rectangle(new Point(),bmp.Size),ImageLockMode.ReadWrite, bmp.PixelFormat);
    5. Marshal.Copy(pixelData,0,bmpData.Scan0,pixelData.Length);
    6. bmp.UnlockBits(bmp);

    könnte noch sein, dass du das PixelFormat selbst festlegen musst, weiß nicht was da standard ist.
    Man könnte wenn man tieferen Eingriff in MonoGame hätte sogar noch ne Kopie mehr sparen, und direkt von der GPU in die Bitmap kopieren, bietet MonoGame halt nicht an^^
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---
    Danke, hat geklappt..
    Nur die Channels mussten geswappt werden:

    C#-Quellcode

    1. for (int i = 0; i < pixelData.Length; i++)
    2. {
    3. pixelData[i] = (int)((pixelData[i] & 0x000000ff) << 16 | (pixelData[i] & 0x0000FF00) | (pixelData[i] & 0x00FF0000) >> 16 | (pixelData[i] & 0xFF000000));
    4. }
    stackoverflow.com/questions/28…om-a-bitmap-object-in-xna
    Und Gott alleine weiß alles am allerbesten und besser.
    ich weiß nicht wie MonoGame damit umgeht, wenn du ein zu großes Array reingibst, aber wenn es um die maximierung der Performance geht, dann kannst du vmtl(auf 64 Bit Systemen) noch etwas Geschwindigkeit bekommen, wenn du auf long Basis arbeitest, dadurch kanns aber natürlich passieren, dass vom letzten long nur die erste hälfte benötigt wird, ob MonoGame das kann keine Ahnung, kannst du aber ja mal probieren und dann evtl. optimieren.
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---
    Hi
    die Zahl der Integer-Operationen wird zusätzlich zur Zahl der Sprünge halbiert, sollte schon etwas rauskommen, bei. Ist aber ggf. eher insignifikant wenig im Vergleich zur restlichen Rendering-Effizienz. In der Regel liegt es nicht an der Kopieroperation, wenn etwas absolut nicht läuft.

    Edit: Ergänzt.

    Viele Grüße
    ~blaze~

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „~blaze~“ ()