views:

2892

answers:

4

In a level editor I've made for my XNA game (editor is also in XNA) I'm allowing to scale Texture2D objects.

When the user tries to save the level, I'd like to actually resize the image file on disk so that it doesn't require scaling in the game.

Is there an easy way to create an image file (PNG preferred) from a scaled Texture2D object?

A: 

Have you tried Texture2D.Save()?

kitchen
This would work if the Texture2D was the correct size, but the scaling is done with the Draw calls, how can I create a Texture2D object that is scaled?
Ben S
A: 

Scaling images generally does not incur all that much overhead, plus, you wouldn't want to load different sizes of the same texture into memory as that would be a waste.

Of course, you could be writing a Zune game, which means I'm probably off about the scaling there, but, anyway.

I don't think you can save the scaled image directly from XNA, but, you could use WinForms Image class to load the image and then scale it to the dimensions in your level editor and save it back out to disk.

If your texture isn't a BMP or PNG though, you may have to use the Texture2D.Save() command to convert it into a format you can use in the Image class.

This thread might help you with using the Image class. http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/6fe3d353-0b09-440b-95c9-701efdc9e20a/

Navaar
I'd really like to avoid scaling in-game to keep my collision code simple.
Ben S
+4  A: 

You can scale your textures by rendering to a render target at the size you want and then saving the render target texture.

This simple example shows how you could do that. Ignore the setup of the GraphicsDevice, that's just to make a small self-contained example. The interesting bit is creating the render target and drawing the scaled texture. You should reuse the render targets where you can (all images of the same size can reuse a render target).

using System;
using System.Runtime.InteropServices;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

class Program
{
    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern IntPtr GetConsoleWindow();

    static void Main(string[] args)
    {
        string sourceImagePath = args[0];
        string destinationImagePath = args[1];
        int desiredWidth = int.Parse(args[2]);
        int desiredHeight = int.Parse(args[3]);

        GraphicsDevice graphicsDevice = new GraphicsDevice(
            GraphicsAdapter.DefaultAdapter,
            DeviceType.Hardware,
            GetConsoleWindow(),
            new PresentationParameters());
        SpriteBatch batch = new SpriteBatch(graphicsDevice);

        Texture2D sourceImage = Texture2D.FromFile(
            graphicsDevice, sourceImagePath);

        RenderTarget2D renderTarget = new RenderTarget2D(
            graphicsDevice, 
            desiredWidth, desiredHeight, 1, 
            SurfaceFormat.Color);

        Rectangle destinationRectangle = new Rectangle(
            0, 0, desiredWidth, desiredHeight);

        graphicsDevice.SetRenderTarget(0, renderTarget);

        batch.Begin();
        batch.Draw(sourceImage, destinationRectangle, Color.White);
        batch.End();

        graphicsDevice.SetRenderTarget(0, null);

        Texture2D scaledImage = renderTarget.GetTexture();
        scaledImage.Save(destinationImagePath, ImageFileFormat.Png);
    }
}
Leaf Garland
This is the closest to what I ended up implementing. Though I didn't use the kernel32 import or the GetConsoloWindow, I simply used my GraphicsDevice from my Game object.
Ben S
A: 

I have code like this that makes a larger/smaller copy of a texture but I get errors after a while. After 30 resizes then I get the exception, not the first time.

Seems to be a rendertarget problem but I am not sure. Wonder if you will get these exceptions too if you run this code a lot too?