i具有一个包含BITMAP
结构的变量。该结构定义如下。
[StructLayoutAttribute(LayoutKind.Sequential)]
public struct BITMAP
{
public Int32 Type;
public Int32 Width;
public Int32 Height;
public Int32 WidthBytes;
public UInt16 Planes;
public UInt16 BitsPixel;
public IntPtr Bits;
}
这种结构没有错。当我尝试使用GDI GetObject
API获取位图时,它会带有真实数据。
当我尝试将其转换为字节数组并再次返回时,问题出现了。
在下面,您会看到如何将结构转换为字节数组。再次工作正常,我在数组中看到数据。
var bitmap = new BITMAP();
var bufferSize = Marshal.SizeOf(bitmap);
GetObject(bitmapHandle, bufferSize, out bitmap);
var bytes = new byte[bitmap.WidthBytes * bitmap.Height];
Marshal.Copy(bitmap.Bits, bytes, 0, bytes.Length);
DeleteObject(bitmapHandle);
//"bytes" now contains the byte array of pixels.
现在,这一切都出现了。稍后,当我尝试将此给定的数组转换回BitmapImage
时,我会得到NotSupportedException
。
这是我转换的代码。
var image = new BitmapImage();
using (var stream = new MemoryStream(bytes))
{
stream.Position = 0;
image.BeginInit();
image.CreateOptions = BitmapCreateOptions.None;
image.CacheOption = BitmapCacheOption.OnDemand;
image.UriSource = null;
image.StreamSource = stream;
image.EndInit(); //it throws the exception here
}
image.Freeze();
例外消息是No imaging component suitable to complete this operation was found
。
我尝试在线寻找解决方案,但没有运气。我似乎无法弄清楚我在做什么错。我认为这是从BITMAP
到字节数组的转换过程,但我不知道。
应该注意的是,BitMapsource也可以。
创建位图数据数组时,您将失败。您正在计算数组大小,只是乘以
bitmap.WidthBytes * bitmap.Height
,这不是位映射的预期来源,因为位映射源需要是一个完整的位图文件结构,其中包括Pixel Data之前的54个字节。
有关位图结构的更多信息:https://www.daubnet.com/en/file-format-bmp
也许您还需要知道像素数据区域的大小受"填充"的影响,该字节位于每个扫描线末端的额外字节,添加到完整的INT32块中以加速位图加载。
如果您只需要bitmapsource(或images库),则可以使用GDI的CreateBitMapIndirect函数和WPF InterOp的Interop imaging.createbitebitmapsourcefromhbitmap方法,类似于此:
static BitmapSource FromBITMAP(ref BITMAP bmp)
{
Int32Rect rect = new Int32Rect(0, 0, bmp.Width, bmp.Height);
IntPtr hbitmap = CreateBitmapIndirect(ref bmp);
if (hbitmap == IntPtr.Zero)
return null;
try
{
return Imaging.CreateBitmapSourceFromHBitmap(hbitmap, IntPtr.Zero, rect, BitmapSizeOptions.FromEmptyOptions());
}
finally
{
DeleteObject(hbitmap);
}
}
[DllImport("gdi32.dll", SetLastError = true)]
public static extern IntPtr CreateBitmapIndirect(ref BITMAP lpbm);
[DllImport("gdi32.dll", SetLastError = true)]
public static extern bool DeleteObject(IntPtr hObject);
这里有问题。Bitmap不是对象,根据GetObject API文档,声称使用像素的字节数组的代码只是不正确的。
如果HGDIOBJ是通过调用Createbsectia的命中图创建的位图的手柄,并且指定的缓冲区足够大,则GetObject函数将返回DibSection结构。此外,DibSection中包含的位图结构的BMBIT成员将包含指向位图位值的指针。如果HGDIOBJ是通过任何其他方式创建的位图的手柄,则GetObject仅返回位图的宽度,高度和颜色格式信息。您可以通过调用getDibit或getbitmapbits函数来获得位图的位值。
所以让我们从头开始。据我所知,您的代码始于所谓的BitMaphandle,我认为这是从某个地方进行的HBITMAP。如果是这样,我认为您真正需要的是一个访问HBITMAP并输出字节数组的功能,而另一个功能则以您描述的方式。这样的东西:
private static byte[] GetBitmapData(IntPtr hBitmap)
{
var source = Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, Int32Rect.Empty, null);
// You may use Bmp, Jpeg or other encoder of your choice
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(source));
var stream = new MemoryStream();
encoder.Save(stream);
return stream.ToArray();
}
private static BitmapSource GetBitmapSource(byte[] data)
{
return BitmapFrame.Create(new MemoryStream(data));
}