I'm currently loading and saving Texture2D
s used as mapped images into a database so they can be preloaded later. Each color channel needs to be the precise color it was when saved after loading. The problem is Texture2D.FromStream
sometimes returns incorrect color channels that are sometimes off by 1 or so. This is unacceptable as the mapped colors are useless if they are incorrect.
The example below provides a situation where an RGB of 255 is changed to an RGB of 254 when the alpha is set at 100. When setting the alpha to 1 or 255 they return as 255 correctly, other alpha values cause the same issue as 100.
Texture2D tex = new Texture2D(GraphicsDevice, 20, 20, false, SurfaceFormat.Color);
Color[] data = new Color[tex.Width * tex.Height];
for (int i = 0; i < data.Length; i++) {
data[i] = new Color(255, 255, 255, 100);
}
tex.SetData<Color>(data);
using (Stream stream = File.Open("TestAlpha.png", FileMode.OpenOrCreate)) {
tex.SaveAsPng(stream, tex.Width, tex.Height);
stream.Position = 0;
tex = Texture2D.FromStream(GraphicsDevice, stream);
tex.GetData<Color>(data);
Console.WriteLine(data[0]); // Returns (R:254 G:254 B:254 A:100)
}
I have confirmed the png has the correct RGB of 255 when looking at the saved image in Paint.NET so it can only be something caused during Texture2D.FromStream
.
Well, I did not find the cause of Texture2D.FromStream
's issue but I found an all-around better way to load Texture2D
s. In this case I load GDI Bitmap
from the stream instead, take its data, and transfer it over to a newly created Texture2D
.
I plan on using this whether or not Texture2D.FromSteam
is fixable but I'd still love to know if anyone knows what's going on with it.
For those of you who are new, make sure to include System.Drawing
as a reference as its not present in XNA projects by default.
public static unsafe Texture2D FromStream(GraphicsDevice graphicsDevice, Stream stream) {
// Load through GDI Bitmap because it doesn't cause issues with alpha
using (Bitmap bitmap = (Bitmap) Bitmap.FromStream(stream)) {
// Create a texture and array to output the bitmap to
Texture2D texture = new Texture2D(graphicsDevice,
bitmap.Width, bitmap.Height, false, SurfaceFormat.Color);
Color[] data = new Color[bitmap.Width * bitmap.Height];
// Get the pixels from the bitmap
Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
BitmapData bmpData = bitmap.LockBits(rect, ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
// Write the pixels to the data buffer
byte* ptr = (byte*) bmpData.Scan0;
for (int i = 0; i < data.Length; i++) {
// Go through every color and reverse red and blue channels
data[i] = new Color(ptr[2], ptr[1], ptr[0], ptr[3]);
ptr += 4;
}
bitmap.UnlockBits(bmpData);
// Assign the data to the texture
texture.SetData<Color>(data);
// Fun fact: All this extra work is actually 50% faster than
// Texture2D.FromStream! It's not only broken, but slow as well.
return texture;
}
}