问题
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?
回答1:
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:
- Construct the original Bitmap from the stream, from the memory, or from the file.
- Create a new Bitmap of the same size, with a pixel format of more than 8 bits-per-pixel (BPP).
- Use the Graphics.FromImage() method to obtain a Graphics object for the second Bitmap.
- Use Graphics.DrawImage() to draw the first Bitmap onto the second Bitmap.
- Use Graphics.Dispose() to dispose of the Graphics.
- Use Bitmap.Dispose() to dispose of the first Bitmap.
Create an Indexed Image
This workaround creates a Bitmap object in an indexed format:
- Construct the original Bitmap from the stream, from the memory, or from the file.
- Create a new Bitmap with the same size and pixel format as the first Bitmap.
- Use the Bitmap.LockBits() method to lock the whole image for both Bitmap objects in their native pixel format.
- Use either the Marshal.Copy function or another memory copying function to copy the image bits from the first Bitmap to the second Bitmap.
- Use the Bitmap.UnlockBits() method to unlock both Bitmap objects.
- Use Bitmap.Dispose() to dispose of the first Bitmap.
回答2:
Known GDI+ bug.
You can't close the MemoryStream
straight away.
Copy the output array to another byte array, then close the stream.
回答3:
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).
回答4:
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;
}
}
来源:https://stackoverflow.com/questions/4671449/how-can-i-write-an-extension-method-that-converts-a-system-drawing-bitmap-to-a-b