2
public static class BitmapHelper {
    public static Bitmap Clone(Bitmap bmp) {
        var rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
        Bitmap newbmp = new Bitmap(rect.Width, rect.Height);
        using (Graphics gfx = Graphics.FromImage(newbmp)) {
            gfx.InterpolationMode = InterpolationMode.High;
            gfx.PixelOffsetMode = PixelOffsetMode.None;
            gfx.DrawImage(bmp, rect, rect, GraphicsUnit.Pixel);
        }
        return newbmp;
    }
}

Most of the time, this does create an identical copy, but if the original Bitmap passed to Clone was created by painting a section from a larger Bitmap where the section extended to the right-most edge of the larger Bitmap, then the copy I get back will have slightly altered pixels on its right-most edge.

What am I doing wrong?

The code that creates the original looks like this:

var SmallerImage = new Bitmap(_sqSize, _sqSize);
using (var gfx = Graphics.FromImage(SmallerImage)) {
    gfx.InterpolationMode = InterpolationMode.High;
    gfx.PixelOffsetMode = PixelOffsetMode.None;
    gfx.DrawImage(LargerImage, 
                  new Rectangle(0, 0, _sqSize, _sqSize), 
                  new Rectangle(p.X, p.Y, _sqSize, _sqSize), 
                  GraphicsUnit.Pixel);
}

The important thing to know there is that Point p and _sqSize are such that the source rectangle (the second rectangle in the DrawImage call) goes right up to the right-most edge of LargerImage.

This same code WILL create an identical copy when Point p and _sqSize are such that the source rectangle does not butt up to the right edge of LargerImage.

Can anyone tell me why this happens?

Or, can someone tell me how to RELIABLY create an EXACT pixel-for-pixel duplicate of a Bitmap? Seems like it ought to be easy, but I can't seem to do it.

new Bitmap(bmp) has the same problems as my Clone method. bmp.Clone() does seem to work, but it has problems under the covers (something about it not being a deep copy -- sharing data with the original) and using it breaks my code in many ways.

  • 3
    I've never seen the `Bitmap(Image)` constructor produce something other than a duplicate of the input image. Are you sure the image you're duplicating doesn't have the same artifacts that the clone object does? In my experience, the code you have to create the smaller bitmap version is much more likely to result in artifacts, especially when you are trying to scale from a subset aligned with the edge of the original, as you are here. It would be helpful to see [a good, _minimal_, _complete_ code example](http://stackoverflow.com/help/mcve) that reliably demonstrates the problem. – Peter Duniho Apr 12 '15 at 08:08
  • Have you tried to set PixelOffsetMode.Half ? It really should matter as you don't do any scaling, but giving it a try won't hurt either. [MSDN](https://msdn.microsoft.com/en-us/library/windows/desktop/ms534169%28v=vs.85%29.aspx): _Consider the pixel in the upper-left corner of an image with address (0, 0). With PixelOffsetModeNone, the pixel covers the area between –0.5 and 0.5 in both the x and y directions; that is, the pixel center is at (0, 0). With PixelOffsetModeHalf, the pixel covers the area between 0 and 1 in both the x and y directions; that is, the pixel center is at (0.5, 0.5)._ – TaW Apr 12 '15 at 09:21
  • For problems of using Clone and their solution see [here](http://stackoverflow.com/questions/12709360/whats-the-difference-between-bitmap-clone-and-new-bitmapbitmap) (Read both answers!!) – TaW Apr 12 '15 at 09:35
  • Graphics.DrawImage() does *not* promise to produce an exact replica of the original image with every pixel *exactly* the same as the original. GDI+ was optimized to cut corners where possible if the difference is not observable to the human eye. This even happens if a simple direct bitblt would be the logical choice. If an exact copy is required then you must use LockBits() and copy yourself. – Hans Passant Apr 12 '15 at 10:20

1 Answers1

0

First, check that the image doesn't already have these artifacts before it is cloned.

Using Clone() is indeed only a shallow copy, but I believe that Clone(Rectangle, PixelFormat) is a deep clone:

public static Bitmap Clone(Bitmap bmp) {
    var newbmp = bmp.Clone(new Rectangle(0, 0, bmp.Width, bmp.Height), bmp.PixelFormat);
    return newbmp;
}

If that doesn't work, try copying the raw data:

[DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)]
public static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);

public static Bitmap Clone(Bitmap bmp) {
    var oldData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat);

    var newBmp = new Bitmap(bmp.Width, bmp.Height, oldData.PixelFormat);
    var newData = newBmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat);
    CopyMemory(newData.Scan0, oldData.Scan0, (uint)(Math.Abs(oldData.Stride) * oldData.Height));
    newBmp.UnlockBits(newData);

    bmp.UnlockBits(oldData);
    return newBmp;
}
Anders Carstensen
  • 2,949
  • 23
  • 23