Why is GDI+ cutting off scaled images?
Asked Answered
H

3

9

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.

Halette answered 19/12, 2010 at 6:51 Comment(5)
Does it also happen if instead of RectangleF you use Rectangle?Killy
Try testing with a high-res image insteadNakamura
@Lambert Originally, I was using Rectangles, and had the same problem. I switched to RectangleF to get rid of having to cast all the time (since, sometimes I'm scaling images down too :) )Halette
@Eric The actual images I am using are much larger. I actually only noticed the problem because I am drawing stuff on top of the image, and due to this problem, it is increasingly offset as the image zooms in.Halette
People can see another sample code here: #20777105Hallette
F
14

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

Fern answered 19/12, 2010 at 7:22 Comment(6)
This sounds like it would be the answer, but (as I edited into my question), it doesn't affect anything...Halette
I've run your code above and it works for me if I add this line: e.Graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half; before drawing the image.Fern
Oh, my answer was a little bit vague, I should have mentioned to use PixelOffsetMode.Half.Fern
Ohh! I see. I've edited your answer to mention that specific fact, and accepted it.Halette
Any idea why Default cuts half pixel and Half restores it? Ancient code error?Hallette
Okay for nearest neighbour interpolation mode, but for other modes now my image is cropped by half a pixel on all 4 sides.Atilt
A
0

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.

Allanson answered 19/12, 2010 at 7:27 Comment(1)
Actually, if I correct by 0.5f in each direction, and leave the width/height alone, it works fine (as I mentioned in the question).Halette
C
0

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;
Commodious answered 23/8, 2023 at 23:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.