10

Using some pretty stock standard C# code to resize an image, and place it on a coloured background

Image imgToResize = Image.FromFile(@"Dejeuner.jpg");
Size size = new Size(768, 1024);
Bitmap b = new Bitmap(size.Width, size.Height);

Graphics g = Graphics.FromImage((Image)b);
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.FillRectangle(Brushes.Green, 0, 0, size.Width, size.Height);

g.DrawImage(imgToResize, new Rectangle(0,150,768, 570));
b.Save("sized_HighQualityBicubic.jpg");

The result has a funny artefact in the 0th and 1st columns of pixels. The 0th column appears to be mixed with the background colour, and the 1st column has been made lighter.

See the top left corner zoomed for high quality bicubic and bicubic.

HighQualityBicubic

Bicubic

..and HighQualityBilinear

HighQualityBilinear

This forum post appears to be someone with the same problem: DrawImage with sharp edges

The sounds like a bug to me? I can understand why the colours would mix at the top of the resized image. But mixing the colours on the left / right edges doesn't make sense. Does anyone know of a fix to prevent these artefacts?

Update: very similar conversation going on in the comments here: GDI+ InterpolationMode

russau
  • 8,928
  • 6
  • 39
  • 49
  • It's anti-aliasing the background along with the image. The idea is to get as high a quality smooth as possible. Some call it a bug, others a feature. Either way, it looks like your question was already answered in the forum thread you linked to. Have you tried `HighQualityBilinear`? – Cody Gray - on strike Jan 23 '11 at 04:32
  • updated the question with HighQualityBilinear. The forum thread only mentions an unmanaged fix, and one poster agrees this is a "long standing bug in GDI+ caused I suppose by some lazy implementer failing to deal properly with the boundary situation". – russau Jan 23 '11 at 05:52
  • 1
    My question is a duplicate of this one: http://stackoverflow.com/questions/1890605/ghost-borders-ringing-when-resizing-in-gdi. I'm happy for my question to be deleted/closed – russau Jan 24 '11 at 06:09

3 Answers3

16

Shamelessly lifting the answer from this question, I found this fixes it:

using (ImageAttributes wrapMode = new ImageAttributes())
{
    wrapMode.SetWrapMode(WrapMode.TileFlipXY);
    g.DrawImage(input, rect, 0, 0, input.Width, input.Height, GraphicsUnit.Pixel, wrapMode);
}
Community
  • 1
  • 1
teedyay
  • 23,293
  • 19
  • 66
  • 73
5

Below is the resulting image of typical HighQualityBicubic resizing (drawn over white background).

You can see semi-transparent pixels at edge lines. You can call it a bug. I think it is just a technical detail of the GDI+. And it is simple to workaround this artifact.

1) Prevent anti-aliasing.

...
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
// add below line
g.CompositingMode = CompositingMode.SourceCopy;
...

With CompositingMode.SourceCopy result image will show visible outline but not anti-aliased with background pixels.

2) Crop semi-transparent area

You can ignore those semi-transparent pixels altogether.

Image imgToResize = Image.FromFile(@"Dejeuner.jpg");
Size size = new Size(768, 1024);
Bitmap b = new Bitmap(size.Width, size.Height);

Graphics g = Graphics.FromImage((Image)b);
g.FillRectangle(Brushes.Green, 0, 0, size.Width, size.Height);

Bitmap b2 = new Bitmap(768 + 8, 570 + 8);
{
    Graphics g2 = Graphics.FromImage((Image)b2);
    g2.Clear(Color.White);
    g2.InterpolationMode = InterpolationMode.HighQualityBicubic;
    g2.DrawImage(imgToResize, new Rectangle(2, 2, 768 + 4, 570 + 4));
}

g.CompositingMode = CompositingMode.SourceCopy;
g.DrawImage(b2, 0, 150, new Rectangle(4, 4, 768, 570), GraphicsUnit.Pixel);
b.Save("sized_HighQualityBicubic.jpg");
9dan
  • 4,222
  • 2
  • 29
  • 44
  • +1 Microsoft explicitly recommends your first solution in the following how-to article: [How to: Use Compositing Mode to Control Alpha Blending](http://msdn.microsoft.com/en-us/library/ffyxebc0.aspx). That article also explains how it's neither a bug nor a "technical artifact". Anti-aliasing an image with its background is the default behavior *by design*. If you want something else, you have to ask for it. – Cody Gray - on strike Jan 23 '11 at 08:49
  • Anti-aliasing does make sense when you a drawing an image onto a larger background. It's arguable if it makes sense when an edge of the image is placed on the edge of background - or a background the same size as the image. – russau Jan 23 '11 at 09:23
  • my understanding of step 2 - `Bitmap` b2 gets the original image @ 2,2. The final draw `DrawImage` cuts from b2 @ 4,4. Will 2px be lost along the top & left edges? I understand that's were the transparent pixels "live", but what I'm doing won't work if any of the original image is lost. – russau Jan 23 '11 at 22:57
  • @russau that's up to you to decide, I think. you could save `b2` bitmap to a file and compare to the original. Because it was interpolated it is not clear that those semi-transparent pixels belong to our intended target image. – 9dan Jan 24 '11 at 02:23
  • 2
    fyi, here's a good approach to remove the transparent pixels: http://stackoverflow.com/questions/1890605/ghost-borders-ringing-when-resizing-in-gdi – russau Jan 26 '11 at 22:46
3

Set the PixelOffsetMode property to HighQuality to get a better blend with the background at the edges.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536