为什么这段代码总是不能创建相同的Bitmap副本?

时间:2015-04-12 07:29:38

标签: c# .net image bitmap

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;
    }
}

大多数情况下,这会创建一个相同的副本,但如果传递给Clone的原始位图是通过从较大的位图绘制一个部分来创建的,其中该部分扩展到较大位图的最右边,则我得到的副本会在最右边的边缘略微改变像素。

我做错了什么?

创建原始代码的代码如下所示:

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);
}

重要的是要知道Point p和_sqSize是这样的,因为源矩形(DrawImage调用中的第二个矩形)直到LargerImage的最右边。

当Point p和_sqSize使得源矩形没有与LargerImage的右边缘对接时,同样的代码会创建一个相同的副本。

有谁能告诉我为什么会这样?

或者,有人可以告诉我如何可靠地创建一个像素的精确像素复制吗?看起来它应该很容易,但我似乎无法做到。

new Bitmap(bmp)与我的克隆方法存在同样的问题。 bmp.Clone()似乎确实有效,但它有问题(有些不是深层复制 - 与原始文件共享数据),使用它会在很多方面破坏我的代码。

1 个答案:

答案 0 :(得分:0)

首先,检查图像在克隆之前是否已经存在这些工件。

使用Clone()确实只是一个浅层副本,但我相信Clone(Rectangle, PixelFormat)是一个深层克隆:

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

如果这不起作用,请尝试复制原始数据:

[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;
}