位图克隆问题

ham*_*eza 8 c# clone exception bitmap

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

    using (Bitmap bmp = new Bitmap("C:\\test.jpg"))
    {
        bmp.RotateFlip(RotateFlipType.Rotate180FlipNone);
        bmp.Save("C:\\test.jpg");
    }
Run Code Online (Sandbox Code Playgroud)

它运行没有任何异常.但考虑一下:

    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");
        }
    }
Run Code Online (Sandbox Code Playgroud)

它以ExternalException结束,并显示以下消息:"GDI +中发生了一般错误".这有什么不对?打开文件的任何类型的锁定?如果是这样,为什么第一个块有效?克隆System.Drawing.Bitmap的正确代码是什么,而我们可能需要在内存中编辑主对象或其克隆,并且仍然将它们都加载到内存中?

jsc*_*arf 5

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

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

更新:

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

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

另外,来自MSDN:

不允许将图像保存到它构造的同一文件中并引发异常

这很尴尬.如果使用创建的对象clone()保留图像源上的信息(例如原始文件上的句柄),或者在使用Bitmap实例时无法解锁文件,那么您可能需要保存到新文件,或者从原件的临时副本打开.

试试这个:

// ... 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();
Run Code Online (Sandbox Code Playgroud)


小智 5

您还可以使用简单的解决方法加载位图而无需文件锁定:

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


Ped*_*o77 5

这就是我复制位图的方法:

[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));
    }
Run Code Online (Sandbox Code Playgroud)

## ImageCopyBenchmark ##

图像大小:{宽度= 1024,高度= 1024}.
Image PixelFormat:Format8bppIndexed.
Bitmap.Clone():0,00 ms(不是DeepCopy ... 相同的像素数据 - 请看这里)
Bitmap.Clone()+ RotateFlip(以获取深拷贝):2,02 ms
KernellDllCopyBitmap:0,52 ms(最好的!)
MarshalCopyBitmap:2,21毫秒