WinUI 3中如何无额外拷贝将IntPtr存储的原始图像数据写入Image控件
WinUI3 避免WriteableBitmap数据额外拷贝的实现方法
问题描述
我在XAML中定义了如下Image控件:
<Image x:Name="image" Stretch="None" Width="640" Height="480"/>
同时持有存储在IntPtr中的BGRA格式640x480图像数据(示例通过读取PNG文件获取):
Bitmap bitmap = new Bitmap("image.png");
已实现通过WriteableBitmap更新Image源的代码,但会产生额外的数据拷贝(从IntPtr转存到byte[]):
int width = 640; int height = 480; WriteableBitmap imageBitmap = new WriteableBitmap(width, height); Bitmap bitmap = new Bitmap("image.png"); BitmapData bitmapData = bitmap.LockBits( new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, bitmap.PixelFormat ); int size = width * height * 4; byte[] managedArray = new byte[size]; Marshal.Copy(bitmapData.Scan0, managedArray, 0, size); bitmap.UnlockBits(bitmapData); imageBitmap.PixelBuffer.AsStream().Write(managedArray, 0, size); this.image.Source = imageBitmap;
由于imageBitmap.PixelBuffer.AsStream().Write不接受IntPtr参数,必须通过byte[]中转。之前在WPF中可通过WritePixels直接实现无额外拷贝操作:
imageBitmap = new WriteableBitmap(width, height, 96d, 96d, PixelFormats.Bgra32, null); Int32Rect rect = new Int32Rect(0, 0, width, height); imageBitmap.WritePixels(rect, bitmapData.Scan0, width * height * 4, stride); this.image.Source = imageBitmap;
想知道WinUI3中如何实现相同的无额外拷贝操作?
解决方案
WinUI3没有提供WPF那样的WritePixels方法,但可通过**IBufferByteAccess COM接口**直接获取WriteableBitmap.PixelBuffer的原始内存指针,实现直接内存拷贝,省略byte[]中转步骤。
1. 定义IBufferByteAccess接口
首先在代码中声明该COM接口:
[ComImport] [Guid("905a0fef-bc53-11df-8c49-001e4fc686da")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public interface IBufferByteAccess { unsafe void Buffer(out byte* value); }
2. 无拷贝写入实现代码
启用不安全代码后,通过接口获取指针并直接拷贝:
int width = 640; int height = 480; int totalSize = width * height * 4; // BGRA格式每个像素4字节 WriteableBitmap imageBitmap = new WriteableBitmap(width, height); Bitmap bitmap = new Bitmap("image.png"); BitmapData bitmapData = bitmap.LockBits( new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb ); try { var bufferAccess = imageBitmap.PixelBuffer as IBufferByteAccess; unsafe { if (bufferAccess != null) { bufferAccess.Buffer(out byte* destPtr); // 直接从原始IntPtr拷贝到WriteableBitmap的内存区域 Marshal.Copy(bitmapData.Scan0, new IntPtr(destPtr), 0, totalSize); } } imageBitmap.Invalidate(); this.image.Source = imageBitmap; } finally { bitmap.UnlockBits(bitmapData); }
注意事项
- 需在项目属性的生成选项中勾选「允许不安全代码」。
- 确保原始图像的像素格式与
WriteableBitmap默认的BGRA8格式匹配,避免格式不兼容导致的显示异常。 - 此方法直接操作内存指针,性能优于中转
byte[]的实现。
内容的提问来源于stack exchange,提问作者Bobbie
相关产品推荐
相关产品推荐

