Seems like the right answer to this is to skip the ContentPipeline and use Texture2D.FromStream to load the textures at runtime. This method works fine in a PC and even though there will be a small performance hit this is something that I can optimize once I'm closer to the release date. For now, having the ability of dynamically modifying the content for both the editor and game are exactly what I need. Once the content is frozen I can optimize this by going back to the ContentPipeline.
Since you've chosen this route I must warn you that it's actually not as simple as just using
Texture2D.FromStream for two reasons:
Problem #1 - Lack of pre-multiplied alpha support
XNA4 now handles textures with colors in premultiplied alpha format by default. When you load a texture through the content pipeline, that processing is done for you automatically. Unfortunately
Texture2D.FromStream doesn't do the same thing, so any textures that require some degree of transparency will be loaded and rendered incorrectly. Below is a screenshot to illustrate the problem:
So in order to get the correct results, you need to do the processing yourself. The method I'll show uses the GPU to do the processing so it's pretty fast. It was based on this great article. Of course you could also instruct
SpriteBatch to render in the old NonPremultiplyAlpha mode but I don't really recommend doing that.
Problem #2 - Unsupported formats
The content pipeline supports more formats than
Texture2D.FromStream. In particular,
Texture2D.FromStream only supports png, jpg and gif. On the other hand, the content pipeline supports bmp, dds, dib, hdr, jpg, pfm, png, ppm, and tga. If you try to load an usuported format through
Texture2D.FromStream you'll get an
InvalidOperationException with little additional information.
I really needed bmp support on my engine so for that particular case I found a workaround that seems to work okay. I don't know about any of the other formats though. The catch with my method is that you need to add a reference to the
System.Drawing assembly to your project, because it makes use of GDI's
Image.FromStream which supports more formats than
If you don't care about supporting bmp you can easily drop that part of my solution and just do the pre-multiplied alpha processing.
Solution - Simple Version (Slower)
First of all, here's the simplest solution if you don't care about supporting bmps. In this example the processing stage is done entirely on the CPU. It's a bit slower than the alternative I'll show below (I did benchmark both solutions) but easier to understand:
public static Texture2D FromStream(GraphicsDevice graphicsDevice, Stream stream)
Texture2D texture = Texture2D.FromStream(graphicsDevice, stream);
Color data = new Color[texture.Width * texture.Height];
for (int i = 0; i != data.Length; ++i)
data[i] = Color.FromNonPremultiplied(data[i].ToVector4());
If you care about bmps then the thing you need to do is load the image with GDI first and then convert into PNG internally before passing it to
Texture2D.FromStream. Here's the code that does that:
// Load image using GDI because Texture2D.FromStream doesn't support BMP
using (Image image = Image.FromStream(stream))
// Now create a MemoryStream which will be passed to Texture2D after converting to PNG internally
using (MemoryStream ms = new MemoryStream())
texture = Texture2D.FromStream(_graphicsDevice, ms);
Solution - Complex Version (Faster)
Finally, the approach I use on my projects is to use the GPU to do the processing instead. In this method you need to create a render target, set up some blend states properly, and draw the image twice with a SpriteBatch. At the end I go over the entire RenderTarget2D and clone the contents into a separate Texture2D object because the RenderTarget2D is volatile and won't survive things like changing the backbuffer size so it's safer to make a copy.
The funny thing is that even with all of this, on my tests this approach performed about 3 times faster than the CPU approach. So it's definitively faster than going over each pixel and calculating the color yourself. The code is a bit long so I placed it in a pastebin:
Just add that class to your project, and using it as simple as:
TextureLoader textureLoader = new TextureLoader(GraphicsDevice);
Texture2D texture = textureLoader.FromFile("Content/texture.png");
Note: You only need to create one
TextureLoader instance for the entire game. Also I'm using the BMP fix but you can take it out if you don't need and gain a bunch of performance, or just leave the
needsBmp parameter as false.