Mike Caron
Mike Caron

Reputation: 14561

Why is GDI+ cutting off scaled images?

I am doing some image scaling using GDI+ (C#), and have noticed a problem where the image I am scaling is being cut off along the left and top edges.

http://zctut.com/cutoff.png

To reproduce this, create a new form project, save this image into the bin\debug folder, and add the following code to the form (and, the corresponding events):

public partial class Form1 : Form {
    public Form1() {
        InitializeComponent();
    }

    int scale = 1;
    Image img = Image.FromFile("circle.png");

    private void Form1_Paint(object sender, PaintEventArgs e) {
        //this makes the glitch easier to see
        e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;

        RectangleF srcRect = new RectangleF(0f, 0f, img.Width, img.Height);
        RectangleF destRect = new RectangleF(0f, 0f, img.Width * scale, img.Height * scale);

        e.Graphics.DrawImage(img, destRect, srcRect, GraphicsUnit.Pixel);
    }

    private void Form1_Click(object sender, EventArgs e) {
        scale++;
        if (scale > 8) scale = 1;
        Invalidate();
    }
}

As you can see, the left- and top-most rows of pixels are being cut off, as if the scaling rectangle is starting half-way in the pixel.

Edit: For note, I also tried using a Scale transform instead of using rectangles as above, and it rendered exactly the same.

Now, that said, I did discover a work around. If you change the rectangle declarations in sample above like this:

RectangleF srcRect = new RectangleF(-0.5f, -0.5f, img.Width, img.Height);

So that we correct for the "half-way" thing, then the image renders correctly.

Basically, while this is easy to work around, am I doing something wrong, or is this normal behaviour?

Edit: As per Andrei Pana's suggestion, I tried adding this code before the drawing call:

e.Graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.None;

And, unfortunately, it did not affect the rendering. The edge was still cut off.

Upvotes: 9

Views: 2402

Answers (3)

John Gietzen
John Gietzen

Reputation: 49575

These are: my go-to setting for high quality and the one I've found works for pixelated images:

// High Quality
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.SmoothingMode = SmoothingMode.HighQuality;

// Pixel Art
g.InterpolationMode = InterpolationMode.NearestNeighbor;
g.PixelOffsetMode = PixelOffsetMode.Half;
g.SmoothingMode = SmoothingMode.None;

Upvotes: 0

Andrei Pana
Andrei Pana

Reputation: 4502

Try setting the PixelOffsetMode to PixelOffsetMode.Half. By default, for high speed anti aliasing, pixels are offset by -0.5

Upvotes: 14

Flipster
Flipster

Reputation: 4401

Set the size of the image to be 2 pixels larger (in each dimension) than the graphic that it contains. I have encountered this as well, and have found that the antialiasing over-shoot is never more than 1 pixel on each side.

In other words, either turn off the anti-aliasing (which will fix this), or change this section of your code:

RectangleF destRect = new RectangleF(0f, 0f, img.Width * scale, img.Height * scale);

to this:

RectangleF destRect = new RectangleF(1f, 1f, img.Width * scale -2, img.Height * scale -2);

(or use an equivalent work-around that uses srcRect)

Yes, this is normal behavior and is a known issue with GDI+/.Net.

Upvotes: 0

Related Questions