Precisely scale image

Viewed 447

I am trying to precisely and predictably scale an image in C# to a different resolution, both up and down. When I open the resulting images with external tools such as Gimp, the results are not satisfying with my current settings.

public Image Square(Image image, int res) {
    Bitmap sq = new Bitmap(res, res, image.PixelFormat);
    Graphics canvas = Graphics.FromImage(sq);
    canvas.CompositingQuality = CompositingQuality.HighQuality;
    canvas.SmoothingMode = SmoothingMode.None;
    canvas.InterpolationMode = InterpolationMode.Bicubic;
    canvas.DrawImage(sq, 0, 0, res, res);
    return sq;
}

The results are okay when scaling down (but far from perfect), but there are side-effects when scaling up:

This picture has a resolution of 2x2 pixels. The alpha channel is set to opaque for all pixels. 2x2 opaque

After scaling it to 4x4 pixels, this is the result: 4x4 with alpha

Apparently, the C# graphics library introduced transparency while scaling the picture. This method should still work if the given image has transparent pictures, so removing the alpha channel is not an option.

Similarly, when scaling pictures down, there are problems at the edges of the resulting images as well, usually either very dark or transparent.

Is there any way to circumvent this behavior?


Edit: I already tried NearestNeighbor for downscaling only, but it results in this:

4x4 nearest neighbor


Edit 2: With WrapMode.TileFlipXY, the transparent edge is gone, but red only makes up 25% of the image instead of 50% as it should be:

4x4 tileflipxy

3 Answers

A way to avoid edge artifacts is to wrap the image:

using (ImageAttributes wrapMode = new ImageAttributes())
{
    wrapMode.SetWrapMode(WrapMode.TileFlipXY);
    g.DrawImage(input, rect, 0, 0, input.Width, input.Height, GraphicsUnit.Pixel, wrapMode);
}

Direct copy/paste from:

Ghost-borders ('ringing') when resizing in GDI+

I believe you need to combine NearestNeighbor interpolation with Half pixel offset. As pointed out in a similar question here.

canvas.InterpolationMode = InterpolationMode.NearestNeighbor;
canvas.PixelOffsetMode = PixelOffsetMode.Half;
Related