C# 位图克隆问题

C# 位图克隆问题,c#,exception,bitmap,clone,C#,Exception,Bitmap,Clone,加载、修改和保存位图图像时,请考虑以下代码: using (Bitmap bmp = new Bitmap("C:\\test.jpg")) { bmp.RotateFlip(RotateFlipType.Rotate180FlipNone); bmp.Save("C:\\test.jpg"); } 它毫无例外地运行。 但是考虑一下这一点: using (Bitmap bmp = new Bitmap("C:\\test.jpg"

加载、修改和保存位图图像时,请考虑以下代码:

    using (Bitmap bmp = new Bitmap("C:\\test.jpg"))
    {
        bmp.RotateFlip(RotateFlipType.Rotate180FlipNone);
        bmp.Save("C:\\test.jpg");
    }
它毫无例外地运行。 但是考虑一下这一点:

    using (Bitmap bmp = new Bitmap("C:\\test.jpg"))
    {
        using (Bitmap bmpClone = (Bitmap)bmp.Clone())
        {
            //You can replace "bmpClone" in the following lines with "bmp",
            //exception occurs anyway                    
            bmpClone.RotateFlip(RotateFlipType.Rotate180FlipNone);
            bmpClone.Save("C:\\test.jpg");
        }
    }
它以一个ExternalException结束,并显示以下消息:“GDI+中发生了一个通用错误”。
这里怎么了?对打开的文件有任何类型的锁定吗?如果是这样的话,为什么第一个模块可以工作?当我们可能需要在内存中编辑主对象或其克隆并仍将它们加载到内存中时,克隆System.Drawing.Bitmap的正确代码是什么?

是的,当加载第一个位图对象时,文件被锁定,因此
bmpClone.Save()
到同一文件失败,因为您有逻辑死锁

按文件名打开位图时,该文件在位图的整个生命周期内被锁定。如果使用流,则流必须保持打开状态

更新

如果希望在内存中有两个位图,以便在所用方法的范围之外使用,则不会使用
using

从文件创建第一个图像,然后克隆它。在整个UI生命周期中根据需要使用它们,但确保在不再需要它们时使用
Dispose()
清理它们,以便释放底层资源

此外,从MSDN:

将图像保存到与其相同的文件中 不允许从中构造 并抛出一个异常

那太尴尬了。如果使用
clone()
创建的对象保留了有关图像源的信息(例如原始文件上的句柄),或者在使用位图实例时无法解锁该文件,则可能需要保存到新文件,或者从原始文件的临时副本打开

请尝试以下方法:

// ... make a copy of test.jpg called test_temp.jpg

Bitmap bmpOriginal = new Bitmap("C:\\test_temp.jpg"))
Bitmap bmpClone = (Bitmap)bmp.Clone();

// ... do stuff          
bmpClone.RotateFlip(RotateFlipType.Rotate180FlipNone);

bmpClone.Save("C:\\test.jpg");

// ... cleanup
bmpOriginal.Dispose();
bmpClone.Dispose();

您也可以使用以下简单的解决方法加载位图而不锁定文件:

using (Stream s = File.OpenRead(@"\My Documents\My Pictures\Waterfall.jpg"))
Bitmap _backImage = (Bitmap)Bitmap.FromStream(s);

这是我复制位图的方式:

[DllImport("kernel32.dll", EntryPoint = "CopyMemory")]
static extern void CopyMemory(IntPtr Destination, IntPtr Source, uint Length);

public static Bitmap KernellDllCopyBitmap(Bitmap bmp, bool CopyPalette = true)
{
    Bitmap bmpDest = new Bitmap(bmp.Width, bmp.Height, bmp.PixelFormat);

    if (!KernellDllCopyBitmap(bmp, bmpDest, CopyPalette))
        bmpDest = null;

    return bmpDest;
}


/// <summary>
/// Copy bitmap data.
/// Note: bitmaps must have same size and pixel format.
/// </summary>
/// <param name="bmpSrc">Source Bitmap</param>
/// <param name="bmpDest">Destination Bitmap</param>
/// <param name="CopyPalette">Must copy Palette</param>
public static bool KernellDllCopyBitmap(Bitmap bmpSrc, Bitmap bmpDest, bool CopyPalette = false)
{
    bool copyOk = false;
    copyOk = CheckCompatibility(bmpSrc, bmpDest);
    if (copyOk)
    {
        BitmapData bmpDataSrc;
        BitmapData bmpDataDest;

        //Lock Bitmap to get BitmapData
        bmpDataSrc = bmpSrc.LockBits(new Rectangle(0, 0, bmpSrc.Width, bmpSrc.Height), ImageLockMode.ReadOnly, bmpSrc.PixelFormat);
        bmpDataDest = bmpDest.LockBits(new Rectangle(0, 0, bmpDest.Width, bmpDest.Height), ImageLockMode.WriteOnly, bmpDest.PixelFormat);
        int lenght = bmpDataSrc.Stride * bmpDataSrc.Height;

        CopyMemory(bmpDataDest.Scan0, bmpDataSrc.Scan0, (uint)lenght);

        bmpSrc.UnlockBits(bmpDataSrc);
        bmpDest.UnlockBits(bmpDataDest);

        if (CopyPalette && bmpSrc.Palette.Entries.Length > 0)
            bmpDest.Palette = bmpSrc.Palette;
    }
    return copyOk;
}

    public static bool CheckCompatibility(Bitmap bmp1, Bitmap bmp2)
    {
        return ((bmp1.Width == bmp2.Width) && (bmp1.Height == bmp2.Height) && (bmp1.PixelFormat == bmp2.PixelFormat));
    }
[DllImport(“kernel32.dll”,EntryPoint=“CopyMemory”)]
静态外部无效复制内存(IntPtr目标、IntPtr源、uint长度);
公共静态位图内核LLDLLCopyBitmap(位图bmp,bool CopyPalette=true)
{
位图bmpDest=新位图(bmp.Width、bmp.Height、bmp.PixelFormat);
if(!kernellllcopyBitmap(bmp、bmpDest、CopyPalette))
bmpDest=null;
返回bmpDest;
}
/// 
///复制位图数据。
///注意:位图必须具有相同的大小和像素格式。
/// 
///源位图
///目标位图
///必须复制调色板
公共静态bool KernellDllCopyBitmap(位图bmpSrc、位图bmpDest、bool CopyPalette=false)
{
bool copyOk=false;
copyOk=检查兼容性(bmpSrc、bmpDest);
如果(copyOk)
{
位图数据bmpDataSrc;
位图数据bmpDataDest;
//锁定位图以获取位图数据
bmpDataSrc=bmpSrc.LockBits(新矩形(0,0,bmpSrc.Width,bmpSrc.Height),ImageLockMode.ReadOnly,bmpSrc.PixelFormat);
bmpDataDest=bmpDest.LockBits(新矩形(0,0,bmpDest.Width,bmpDest.Height),ImageLockMode.WriteOnly,bmpDest.PixelFormat);
int lenght=bmpDataSrc.Stride*bmpDataSrc.Height;
CopyMemory(bmpDataDest.Scan0,bmpDataSrc.Scan0,(uint)长度);
bmpSrc.解锁位(bmpDataSrc);
bmpDest.解锁位(bmpDataDest);
如果(CopyPalette&&bmpSrc.Palette.Entries.Length>0)
bmpDest.palete=bmpSrc.palete;
}
返回copyOk;
}
公共静态布尔检查兼容性(位图bmp1、位图bmp2)
{
返回((bmp1.Width==bmp2.Width)&&(bmp1.Height==bmp2.Height)&&(bmp1.PixelFormat==bmp2.PixelFormat));
}
###ImageCopyBenchmark#

图像大小:{Width=1024,Height=1024}。
图像像素格式:Format8Bppined.
Bitmap.Clone():0,00毫秒(不是深度复制…)
Bitmap.Clone()+RotateFlip(用于获取深度拷贝):2,02毫秒
KernellDllCopyBitmap:0,52毫秒(最好!)

MarshallCopyBitmap:2,21毫秒

以下是我的化妆方法:

private static unsafe Bitmap DuplicateBitmap(Bitmap inputBitmap) { byte[] buffer = new byte[inputBitmap.Height * inputBitmap.Width * Image.GetPixelFormatSize(inputBitmap.PixelFormat) / 8]; fixed (byte* p = buffer) { BitmapData b1Data = new BitmapData() { Scan0 = (IntPtr)p, Height = inputBitmap.Height, Width = inputBitmap.Width, PixelFormat = inputBitmap.PixelFormat, Stride = inputBitmap.Width * Image.GetPixelFormatSize(inputBitmap.PixelFormat) / 8, }; inputBitmap.LockBits(new Rectangle(Point.Empty, inputBitmap.Size), ImageLockMode.ReadOnly | ImageLockMode.UserInputBuffer, inputBitmap.PixelFormat, b1Data); // copy out. Bitmap b2 = new Bitmap(b1Data.Width, b1Data.Height, b1Data.Stride, inputBitmap.PixelFormat, b1Data.Scan0); inputBitmap.UnlockBits(b1Data); return b2; } } 私有静态不安全位图复制位图(位图输入位图) { 字节[]缓冲区=新字节[inputBitmap.Height*inputBitmap.Width]* Image.GetPixelFormatSize(inputBitmap.PixelFormat)/8]; 固定(字节*p=缓冲区) { BitmapData b1Data=新的BitmapData() { Scan0=(IntPtr)p, 高度=输入位图。高度, 宽度=输入位图。宽度, PixelFormat=inputBitmap.PixelFormat, Stride=inputBitmap.Width*Image.GetPixelFormatSize(inputBitmap.PixelFormat)/8, }; inputBitmap.LockBits(新矩形(Point.Empty,inputBitmap.Size), ImageLockMode.ReadOnly | ImageLockMode.UserInputBuffer,inputBitmap.PixelFormat,b1Data);//复制。 位图b2=新位图(b1Data.Width、b1Data.Height、b1Data.Stride、inputBitmap.PixelFormat、b1Data.Scan0); 输入位图。解锁位(b1Data); 返回b2; } }
速度提高10%(取决于位图大小…

如果使用
位图bmpClone=new bitmap(bmp)
,有什么区别吗?@dtb:没有,出现相同的异常。确定!但是,如果我们真的需要在内存中有两个位图副本(两个图片框,一个显示“原始”图像,一个显示“修改”图像,无论何时,我们的用户都可以单击“保存”按钮),该怎么办?然后创建两个克隆,并保持一个未修改。@dtb:保存方法出现问题,在第二个块中,“bmp”仍然未修改。一种解决方案是:创建一个新位图,其宽度+高度+像素格式与源文件相同,并在其上绘制源文件(自己实现克隆),但这种解决方案明智吗?什么是高效、快速和标准的解决方案?当位图可能导致这样的异常时,克隆位图如何有用?
新建位图(位图)
正是这样做的:创建一个与源相同宽度+高度+像素格式的新位图,并在其上绘制源it@dtb:不能,因为在位图画布上绘制并不总是创建cop的选项