I'd change 2 things with that code:
1. Call g.Dispose() before you leave the function; this will free up the resources used by the Graphics object. It will happen eventually, but garbage collection can take a while.
2. The default interpolation isn't very good. Setting
Code:
g.InterpolationMode = InterpolationMode.Bicubic;
(Or one of the other options if preferred) would be better.
Here's a piece of my code that does this. I pass the image by reference because I really don't care about the original (and I don't have any other references to the original apart from the one I pass in), so I trash the original and return the new image. If you cared about keeping the original image, you could of course do it your way, where it takes an image as a parameter and returns a new image as a return value.
Mine also has a preserve aspect ratio option -- that is, it will never return an image wider than w or taller than h, but it will keep the aspect ratio fixed if you set the fourth parameter to true. So e.g. a 300x600 image that was set to resize to 200x200 with aspect ratio preserved would actually become 100x200.
Code:
private void ResizeImage(ref Image i, int w, int h, bool preserveAspectRatio, InterpolationMode mode)
{
int realWidth = w;
int realHeight = h;
if (preserveAspectRatio)
{
double hscale = (double)w / (double)i.Width;
double vscale = (double)h / (double)i.Height;
double scale = (hscale < vscale) ? hscale : vscale;
// Rounded to nearest pixel
realWidth = (int)(i.Width * scale + 0.5);
realHeight = (int)(i.Height * scale + 0.5);
}
// Since copying or scaling a bitmap will use a crappy quality,
// we need to make a new bitmap of the correct size, get the associated Graphic Context,
// set the quality, and make a copy:
Bitmap j = new Bitmap(realWidth, realHeight); // New image
Graphics g = Graphics.FromImage(j);
g.InterpolationMode = mode;
g.DrawImage(i, new Rectangle(0, 0, realWidth, realHeight));
i.Dispose(); // Destroy the old image
g.Dispose();
i = j; // Return the new one in our reference parameter.
}