7

考虑以下用于加载、修改和保存位图图像的代码:

    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 的正确代码,而我们可能需要在内存中编辑主对象或其克隆并且仍然将它们都加载到内存中?

4

4 回答 4

5

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

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

更新

如果您希望在内存中使用两个位图以在您所使用的方法范围之外使用,那么您将不会使用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();
于 2009-08-23T18:04:30.380 回答
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));
    }

## ImageCopyBenchmark ##

图像尺寸:{宽度=1024,高度=1024}。
图像像素格式:Format8bppIndexed。
Bitmap.Clone():0,00 毫秒(不是 DeepCopy ......相同的像素数据 - 看这里
Bitmap.Clone() + RotateFlip(获取深层副本):2,02 毫秒
KernellDllCopyBitmap:0,52 毫秒(最好的!)
MarshalCopyBitmap:2.21 毫秒

于 2013-05-03T15:27:45.550 回答
4

您还可以使用简单的解决方法在不锁定文件的情况下加载位图:

using (Stream s = File.OpenRead(@"\My Documents\My Pictures\Waterfall.jpg"))
Bitmap _backImage = (Bitmap)Bitmap.FromStream(s);
于 2009-09-08T13:21:29.513 回答
0

这是我的虚荣方法:

    私有静态不安全位图 DuplicateBitmap(Bitmap inputBitmap)
    {
        字节[] 缓冲区 = 新字节[inputBitmap.Height * inputBitmap.Width *
                            Image.GetPixelFormatSize(inputBitmap.PixelFormat) / 8];  

        固定(字节* p = 缓冲区)
        {
            位图数据 b1Data = 新位图数据()
            {
                Scan0 = (IntPtr)p,
                高度 = inputBitmap.Height,
                宽度 = inputBitmap.Width,
                PixelFormat = inputBitmap.PixelFormat,
                步幅 = inputBitmap.Width * Image.GetPixelFormatSize(inputBitmap.PixelFormat) / 8,
            };

            inputBitmap.LockBits(new Rectangle(Point.Empty, inputBitmap.Size),
                ImageLockMode.ReadOnly | ImageLockMode.UserInputBuffer, inputBitmap.PixelFormat, b1Data); //复制出来。   

            位图 b2 = new Bitmap(b1Data.Width, b1Data.Height, b1Data.Stride, inputBitmap.PixelFormat, b1Data.Scan0);

            inputBitmap.UnlockBits(b1Data);

            返回 b2;
        }
    }

快 10%(取决于位图大小...)

于 2014-11-28T22:19:39.410 回答