如何编写将System.Drawing.Bitmap转换为字节数组的扩展方法?

时间:2011-01-12 16:47:51

标签: .net bitmap gdi+ extension-methods

如何编写将System.Drawing.Bitmap转换为字节数组的扩展方法?为什么不:

<Extension()> _
Public Function ToByteArray(ByVal image As System.Drawing.Bitmap) As Byte()
    Using ms = New MemoryStream()
        image.Save(ms, image.RawFormat)
        Return ms.ToArray()
    End Using
End Function

然而,当我使用它时,我从Save()操作中抛出“System.Runtime.InteropServices.ExternalException:GDI +中发生了一般错误”。我做错了什么?

4 个答案:

答案 0 :(得分:4)

正如其他人所说,这是一个已知的GDI +错误。

但是,它通常会在您完全读取之前关闭图像的流时出现。只是加载一个新的Image对象只会加载元数据,如宽度,高度,颜色深度等,而不是实际的像素。它们将在以后延迟装载。

可以通过将图像(在加载期间)复制到在内存中创建的新图像来避免这种情况。我认为输入流在那时仍然可用。一旦有了新的基于内存的Image类,就可以自由地处理源流。 (另一种解决方案是不关闭/处置源流)。

编辑: KB814675 Bitmap and Image constructor dependencies中描述的问题以及解决方法。

  

创建非索引图像

     

这种方法要求新图像采用非索引像素格式(每像素超过8位),即使原始图像采用索引格式。此解决方法使用Graphics.DrawImage()方法将图像复制到新的Bitmap对象:

     
      
  1. 从流,内存或文件构造原始位图。
  2.   
  3. 创建一个相同大小的新位图,像素格式超过每像素8位(BPP)。
  4.   
  5. 使用Graphics.FromImage()方法获取第二个Bitmap的Graphics对象。
  6.   
  7. 使用Graphics.DrawImage()将第一个Bitmap绘制到第二个Bitmap上。
  8.   
  9. 使用Graphics.Dispose()处理Graphics。
  10.   
  11. 使用Bitmap.Dispose()处理第一个Bitmap。
  12.         

    创建索引图像

         

    此解决方法以索引格式创建Bitmap对象:

         
        
    1. 从流,内存或文件构造原始位图。
    2.   
    3. 创建一个与第一个Bitmap具有相同大小和像素格式的新位图。
    4.   
    5. 使用Bitmap.LockBits()方法以原生像素格式锁定两个Bitmap对象的整个图像。
    6.   
    7. 使用Marshal.Copy函数或其他内存复制功能将图像位从第一个位图复制到第二个位图。
    8.   
    9. 使用Bitmap.UnlockBits()方法解锁两个Bitmap对象。
    10.   
    11. 使用Bitmap.Dispose()处理第一个Bitmap。
    12.   

答案 1 :(得分:1)

已知的GDI +错误。

您无法立即关闭MemoryStream

将输出数组复制到另一个字节数组,然后关闭该流。

答案 2 :(得分:0)

尝试将image.RawFormat更改为JPEG或PNG。某些图像可以通过位图打开但不可保存(至少以原始格式)。

答案 3 :(得分:0)

根据该线程,我使用以下代码,并且可以正常工作:

using (var ms = new MemoryStream(bytes))
{
    using (var bitmap = new Bitmap(ms))
    {
        var bitmapCopy = new Bitmap(bitmap, bitmap.Width, bitmap.Height);
        return bitmapCopy;
    }
}