Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I have a small problem with converting a bitmap to a Texture2D. The resulted image of the conversion has the red channel switched with the blue channel :/ I don't know why, because the pixel formats are the same. If someone can help me I will be very happy :)

      System.Drawing.Image image = 
            System.Drawing.Bitmap.FromFile(ImageFileLoader.filename);
        System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(image);
        Texture2D mapTexture = new Texture2D(Screen.Game.GraphicsDevice, bitmap.Width, bitmap.Height,false,SurfaceFormat.Color);

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

        byte[] bytes = new byte[data.Height * data.Width*4];



        System.Runtime.InteropServices.Marshal.Copy(data.Scan0, bytes, 0, bytes.Length);

        mapTexture.SetData<byte>(bytes, 0, data.Height * data.Width * 4);


        bitmap.UnlockBits(data);
        bitmap.Dispose();
        image.Dispose();
share|improve this question
add comment

3 Answers

up vote 6 down vote accepted

What you are seeing is not unusual. Image data is often stored as either BGR or RGB. (Plus there's ARGB vs RGBA and also premultiplication to take into consideration.)

XNA's SurfaceFormat.Color is ARGB.

For XNA 4 you generally want premultiplied alpha (this has more to do with what blend settings you use or need to use). If your image is entirely opaque it won't matter.

I'm pretty sure that the problem in your code arises from your use of LockBits. While LockBits gives you an opportunity to select a pixel format, it doesn't actually perform any conversion. So you've basically told it that a ABGR image is ARGB - you've lied to it! (I believe ABGR is a common format for Windows bitmaps.)

The solution is, of course, to swap the Red and Blue bytes in your temporary buffer before you set them on the XNA texture.

share|improve this answer
add comment

This works:

// Assuming:
uint[] m_PixelsBuffer;
System.Drawing.Bitmap m_Bitmap;

unsafe {
    System.Drawing.Imaging.BitmapData origdata =
        m_Bitmap.LockBits(new System.Drawing.Rectangle(0, 0, m_Bitmap.Width, m_Bitmap.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, m_Bitmap.PixelFormat);
    uint* byteData = (uint*)origdata.Scan0;
    for (int i = 0; i < m_PixelsBuffer.Length; i++) {
        m_PixelsBuffer[i] = (byteData[i] & 0x000000ff) << 16 | (byteData[i] & 0x0000FF00) | (byteData[i] & 0x00FF0000) >> 16 | (byteData[i] & 0xFF000000);
    }
    m_Bitmap.UnlockBits(origdata);
}
texture.SetData(m_PixelsBuffer);
share|improve this answer
add comment

Here's a method i figured out, that is fast and swaps the red and blue fast also:

    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;
    }
share|improve this answer
    
This code is wrong. First you are modifying the original bitmap itself, and copying with Marshal.Copy is useless since you are already going through all pixels manually. See my correction. –  trethaller May 20 '12 at 21:27
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.