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

发布于 2024-10-11 08:36:20 字数 413 浏览 5 评论 0原文

如何编写将 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+ 中发生一般错误”。我做错了什么?

How can I write an extension method that converts a System.Drawing.Bitmap to a byte array? Why not:

<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

Yet when I use that, I get "System.Runtime.InteropServices.ExternalException: A generic error occurred in GDI+" thrown from the Save() operation. What am I doing wrong?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

晨与橙与城 2024-10-18 08:36:20

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

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

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

编辑:
KB814675 位图和图像构造函数依赖项中描述的问题以及解决方法。

创建非索引图像

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

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

创建索引图像

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

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

As someone else state, this is a known GDI+ bug.

However, it usually appear when you've closed the source stream of the image before reading it completely. Just loading a new Image object will only load metadata, like width, height, color depth, etc, not the actual pixels. They will be lazily loaded at a later time.

This can be avoided by copying your image (during loading) into a new Image created in memory. I presume that the input stream is still available at that time. Once you have the new memory-based Image class you can dispose of the source stream freely. (Another solution would be not to close/dispose the source stream).

Edit:
Problem described in KB814675 Bitmap and Image constructor dependencies together with a workaround.

Create a Non-Indexed Image

This approach requires that the new image be in a non-indexed pixel format (more than 8 bits-per-pixel), even if the original image was in an indexed format. This workaround uses the Graphics.DrawImage() method to copy the image to a new Bitmap object:

  1. Construct the original Bitmap from the stream, from the memory, or from the file.
  2. Create a new Bitmap of the same size, with a pixel format of more than 8 bits-per-pixel (BPP).
  3. Use the Graphics.FromImage() method to obtain a Graphics object for the second Bitmap.
  4. Use Graphics.DrawImage() to draw the first Bitmap onto the second Bitmap.
  5. Use Graphics.Dispose() to dispose of the Graphics.
  6. Use Bitmap.Dispose() to dispose of the first Bitmap.

Create an Indexed Image

This workaround creates a Bitmap object in an indexed format:

  1. Construct the original Bitmap from the stream, from the memory, or from the file.
  2. Create a new Bitmap with the same size and pixel format as the first Bitmap.
  3. Use the Bitmap.LockBits() method to lock the whole image for both Bitmap objects in their native pixel format.
  4. Use either the Marshal.Copy function or another memory copying function to copy the image bits from the first Bitmap to the second Bitmap.
  5. Use the Bitmap.UnlockBits() method to unlock both Bitmap objects.
  6. Use Bitmap.Dispose() to dispose of the first Bitmap.
梦一生花开无言 2024-10-18 08:36:20

已知的 GDI+ 错误。

您无法立即关闭 MemoryStream

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

Known GDI+ bug.

You can't close the MemoryStream straight away.

Copy the output array to another byte array, then close the stream.

云朵有点甜 2024-10-18 08:36:20

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

Try changing image.RawFormat to something like JPEG or PNG. It's possible for some images to be openable by a Bitmap but not saveable (at least in the original format).

一枫情书 2024-10-18 08:36:20

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

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

According to this thread, I using following code and it work correctly:

using (var ms = new MemoryStream(bytes))
{
    using (var bitmap = new Bitmap(ms))
    {
        var bitmapCopy = new Bitmap(bitmap, bitmap.Width, bitmap.Height);
        return bitmapCopy;
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文