A correct answer can be pieced together from some of the other responses, but none of them is complete and some present some very bad ideas (like drawing the image twice).
The problem
There are three reasons for the artifacts you're seeing:
- The default
Graphics.PixelOffsetMode setting causes the pixel values to be sampled incorrectly, resulting in a slight distortion of the image, particularly around the edges.
InterpolationMode.HighQualityBicubic samples pixels from beyond the image edge, which are transparent by default. Those transparent pixels are mixed with the edge pixels by the sampler, resulting in semi-transparent edges.
- When you save a semi-transparent image in a format that doesn't support transparency (e.g. JPEG), the transparent values are replaced by black.
That all adds up to semi-black (i.e. grey) edges.
There are a few other issues with the code you posted as well:
The Bitmap constructor you used is initializing the new Bitmap by resizing the original image, so you're doing the resize operation twice. You should use a constructor overload with just the desired dimensions to create a blank canvas.
Remember that the Bitmap class represents an unmanaged copy of the image in memory. It needs to be disposed so that GDI+ can be told to release that memory when you're done with it. I assume you're doing that in the code that receives the return Image, but I point that out in case anyone else borrows this code.
The CompositingQuality.HighQuality setting used in your code will have no visual effect if you get the other settings right and will actually hurt performance fairly significantly in combination with the default value of CompositingMode.SourceOver. You can omit the CompositingQuality setting and set CompositingMode.SourceCopy to get the same results with better performance.
The SmoothingMode setting used in your code has no impact at all on DrawImage(), so it can be removed.
Solution
The correct way to remove those artifacts is to use PixelOffsetMode.Half and to use an ImageAttributes object to specify edge tiling so the HighQualityBicubic sampler has something other than transparent pixels to sample.
You can read more about the Graphics class settings and their impact on image quality and performance here: http://photosauce.net/blog/post/image-scaling-with-gdi-part-3-drawimage-and-the-settings-that-affect-it
The revised code should look something like this:
public static Image Scale(Image sourceImage, int destWidth, int destHeight)
{
var toReturn = new Bitmap(destWidth, destHeight);
using (var graphics = Graphics.FromImage(toReturn))
using (var attributes = new ImageAttributes())
{
toReturn.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
attributes.SetWrapMode(WrapMode.TileFlipXY);
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.PixelOffsetMode = PixelOffsetMode.Half;
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.DrawImage(sourceImage, Rectangle.FromLTRB(0, 0, destWidth, destHeight), 0, 0, sourceImage.Width, sourceImage.Height, GraphicsUnit.Pixel, attributes);
}
return toReturn;
}