A Texture2D in XNA can only hold so much. Depending on which profile I use that limit is either 2048 or 4096 pixels wide. As far as I could tell from my research (someone correct me if I'm wrong) GDI Images do not have any limitation other than not exceeding the user's memory.
I'm looking for the most efficient way to take a GDI Image and a source rectangle (within XNA's size limitations) and create a new Texture2D from that data at runtime. The process must also take into account XNA 4.0's pre-multiplied alpha!
OR...
A way to take Texture2D.FromStream and adapt it so that it takes a source rectangle too, as that would be even better for my needs.
The requirements are:
- No content pipeline, I need to be able to load them at runtime.
- Using only Reach profile if possible!
- Only care about Windows. Don't worry about portability.
TL;DR:
I need a method such as:
Texture2D Texture2DHelper.FromStream(GraphicsDevice, Stream, SourceRectangle);
Or
Texture2D Texture2DHelper.FromImage(Image, SourceRectangle)
Preferably the first.
Big picture:
Ignoring the source rectangle for a moment (to make things easier to visualize), I've tried loading a whole texture in GDI and passing the data to a Texture2D using a completely a brute-force approach:
using (System.Drawing.Image image = System.Drawing.Image.FromFile(path))
{
int w = image.Width;
int h = image.Height;
System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(image);
uint[] data = new uint[w * h];
for (int i=0; i!=bitmap.Width; ++i)
{
for (int j=0; j!=bitmap.Height; ++j)
{
System.Drawing.Color pixel = bitmap.GetPixel(i, j);
Microsoft.Xna.Framework.Color color = Color.FromNonPremultiplied(pixel.R, pixel.G, pixel.B, pixel.A);
data[i + j * w] = color.PackedValue;
}
}
_texture = new Texture2D(device, w, h);
_texture.SetData(data);
}
And the result was really slow. It took many many times longer than simply doing:
Texture2D.FromStream(GraphicsDevice, new FileStream(path, FileMode.Open));
I also thought about using Bitmap.LockBits and a Block Copy but doing that would leave out the pre-multiplied alpha correction, and I don't know how to apply it after the copy.
I'm tempted to think that a better solution would be to work around Texture2D.FromStream directly... I'm not too familiar with how Stream works but I've seen some methods in .NET that take a Stream and a Rectangle. How would I achieve something like that? Either an entirely new FromStream method or a wrapper around any Stream that provides "Rectangle" functionality.