C# 为什么';这段代码是否总是创建位图的相同副本?

C# 为什么';这段代码是否总是创建位图的相同副本?,c#,.net,image,bitmap,C#,.net,Image,Bitmap,大多数情况下,这确实会创建一个相同的副本,但是如果传递给克隆的原始位图是通过从较大位图绘制一个部分创建的,该部分延伸到较大位图的最右边缘,那么我得到的副本的最右边缘上的像素将略有改变 我做错了什么 创建原始文件的代码如下所示: public static class BitmapHelper { public static Bitmap Clone(Bitmap bmp) { var rect = new Rectangle(0, 0, bmp.Width, bmp.He

大多数情况下,这确实会创建一个相同的副本,但是如果传递给克隆的原始位图是通过从较大位图绘制一个部分创建的,该部分延伸到较大位图的最右边缘,那么我得到的副本的最右边缘上的像素将略有改变

我做错了什么

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

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;
    }
}
需要知道的重要一点是,p点和_sqSize点使得源矩形(DrawImage调用中的第二个矩形)正好到达LargerImage的最右边缘

当p点和_sqSize点使得源矩形不与LargerImage的右边缘对接时,相同的代码将创建相同的副本

谁能告诉我为什么会这样

或者,有人能告诉我如何可靠地创建位图的精确逐像素副本吗?看起来应该很容易,但我似乎做不到


新位图(bmp)
与我的克隆方法存在相同的问题
bmp.Clone()
看起来确实有效,但它在封面下有问题(它不是一个深度拷贝——与原件共享数据),使用它会在许多方面破坏我的代码。

首先,检查图像在克隆之前是否还没有这些工件

使用
Clone()
确实只是一个浅拷贝,但我相信
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);
}
如果不起作用,请尝试复制原始数据:

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

我从未见过
位图(图像)
构造函数除了生成输入图像的副本之外,还生成其他内容。您确定要复制的图像没有与克隆对象相同的瑕疵吗?根据我的经验,您必须创建较小位图版本的代码更有可能产生瑕疵,尤其是当您试图从与原始位图边缘对齐的子集进行缩放时,正如您所处的位置。这将有助于可靠地演示问题。您是否尝试设置PixelOffsetMode。一半?这真的很重要,因为你不做任何缩放,但尝试一下也不会有什么坏处考虑具有地址(0, 0)的图像左上角的像素。使用PixelOffsetModeNone,像素覆盖x和y方向上–0.5和0.5之间的区域;也就是说,像素中心位于(0,0)。使用PixelOffsetModeHalf,像素覆盖x和y方向上0和1之间的区域;也就是说,像素中心位于(0.5,0.5)。有关使用克隆及其解决方案的问题,请参阅(读取两个答案!!)Graphics.DrawImage()不保证生成与原始图像完全相同的每个像素的精确副本。GDI+经过优化,以在人眼无法观察到差异的情况下尽可能地简化操作。如果一个简单的直接bitblt是逻辑选择,这种情况甚至会发生。如果需要精确的副本,则必须使用LockBits()并自行复制。
[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;
}