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
(Or one of the other options if preferred) would be better.
g.InterpolationMode = InterpolationMode.Bicubic;
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.
private void ResizeImage(ref Image i, int w, int h, bool preserveAspectRatio, InterpolationMode mode)
int realWidth = w;
int realHeight = h;
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
i = j; // Return the new one in our reference parameter.