UWP中如何高效从已有SoftwareBitmap的区域创建新的深拷贝实例
高效裁剪并深拷贝UWP SoftwareBitmap指定区域的最优方案
我刚好研究过这个痛点——UWP原生的SoftwareBitmap确实没提供直接裁剪指定区域并深拷贝的方法,之前用WriteableBitmap的两次拷贝加Blit的方式确实有点冗余。这里有两个更高效的方案,你可以根据自己的场景选:
方案一:纯UWP原生API(无额外依赖)
这个方案直接操作像素缓冲区,只拷贝你需要的区域,避免全图拷贝的开销。核心思路是锁定源和目标的缓冲区,逐行拷贝指定区域的像素数据:
实现步骤
- 锁定源
SoftwareBitmap的缓冲区,获取像素数据的内存访问权限; - 根据指定的
BitmapBounds计算源区域的起始偏移、每行的字节跨度(Stride); - 创建匹配源格式、尺寸为裁剪区域大小的目标
SoftwareBitmap; - 锁定目标缓冲区,将源区域的像素数据逐行拷贝到目标缓冲区。
代码示例
using System.Runtime.InteropServices; using Windows.Graphics.Imaging; public static SoftwareBitmap CropSoftwareBitmap(SoftwareBitmap sourceBitmap, BitmapBounds cropBounds) { // 校验参数:确保裁剪区域在源位图范围内 if (cropBounds.X < 0 || cropBounds.Y < 0 || cropBounds.X + cropBounds.Width > sourceBitmap.PixelWidth || cropBounds.Y + cropBounds.Height > sourceBitmap.PixelHeight) { throw new ArgumentOutOfRangeException(nameof(cropBounds), "裁剪区域超出源位图范围"); } // 创建目标SoftwareBitmap var targetBitmap = new SoftwareBitmap( sourceBitmap.BitmapPixelFormat, (int)cropBounds.Width, (int)cropBounds.Height, sourceBitmap.BitmapAlphaMode); // 锁定源和目标缓冲区 using (var sourceBuffer = sourceBitmap.LockBuffer(BitmapBufferAccessMode.Read)) using (var targetBuffer = targetBitmap.LockBuffer(BitmapBufferAccessMode.Write)) { // 获取源缓冲区的内存引用 var sourceReference = sourceBuffer.CreateReference(); byte* sourceBytes; uint sourceCapacity; ((IMemoryBufferByteAccess)sourceReference).GetBuffer(out sourceBytes, out sourceCapacity); // 获取目标缓冲区的内存引用 var targetReference = targetBuffer.CreateReference(); byte* targetBytes; uint targetCapacity; ((IMemoryBufferByteAccess)targetReference).GetBuffer(out targetBytes, out targetCapacity); // 计算源和目标的每行字节数(Stride) var sourceStride = sourceBuffer.GetPlaneDescription(0).Stride; var targetStride = targetBuffer.GetPlaneDescription(0).Stride; var bytesPerPixel = sourceBuffer.GetPlaneDescription(0).BytesPerPixel; // 计算源区域的起始字节偏移 var sourceStartOffset = (int)(cropBounds.Y * sourceStride + cropBounds.X * bytesPerPixel); // 逐行拷贝像素数据 for (int y = 0; y < cropBounds.Height; y++) { // 拷贝当前行的指定区域像素 Buffer.MemoryCopy( sourceBytes + sourceStartOffset + y * sourceStride, targetBytes + y * targetStride, targetCapacity - (uint)(y * targetStride), (uint)(cropBounds.Width * bytesPerPixel)); } } return targetBitmap; } // 需要引入COM接口以访问内存缓冲区 [ComImport] [Guid("5B0D3235-4DBA-4D44-865E-8F1D0E4FD04D")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface IMemoryBufferByteAccess { void GetBuffer(out byte* buffer, out uint capacity); }
注意:这个方法需要在项目中启用“允许不安全代码”(在项目属性的生成选项里勾选),因为用到了指针操作。
方案二:使用Win2D库(更高效简洁)
如果可以引入微软官方的Win2D图形库,这会是更优的选择——Win2D基于Direct2D,底层硬件加速,代码也更简洁,不需要手动操作内存指针。
实现步骤
- 安装Win2D.uwp NuGet包;
- 将源
SoftwareBitmap转换为CanvasBitmap; - 创建一个尺寸为裁剪区域大小的
CanvasRenderTarget; - 在
CanvasRenderTarget上绘制源CanvasBitmap的指定区域; - 将
CanvasRenderTarget转换回SoftwareBitmap。
代码示例
using Microsoft.Graphics.Canvas; using Windows.Graphics.Imaging; public static async Task<SoftwareBitmap> CropSoftwareBitmapWithWin2D(SoftwareBitmap sourceBitmap, BitmapBounds cropBounds) { // 校验参数 if (cropBounds.X < 0 || cropBounds.Y < 0 || cropBounds.X + cropBounds.Width > sourceBitmap.PixelWidth || cropBounds.Y + cropBounds.Height > sourceBitmap.PixelHeight) { throw new ArgumentOutOfRangeException(nameof(cropBounds), "裁剪区域超出源位图范围"); } // 创建CanvasDevice(Win2D的设备上下文) using var device = CanvasDevice.GetSharedDevice(); // 将源SoftwareBitmap转换为CanvasBitmap using var sourceCanvasBitmap = await CanvasBitmap.CreateFromSoftwareBitmapAsync(device, sourceBitmap); // 创建目标尺寸的CanvasRenderTarget using var renderTarget = new CanvasRenderTarget( device, (float)cropBounds.Width, (float)cropBounds.Height, sourceCanvasBitmap.Dpi); // 在RenderTarget上绘制裁剪区域 using var ds = renderTarget.CreateDrawingSession(); ds.DrawImage( sourceCanvasBitmap, new Rect(0, 0, cropBounds.Width, cropBounds.Height), new Rect(cropBounds.X, cropBounds.Y, cropBounds.Width, cropBounds.Height)); // 转换回SoftwareBitmap return await renderTarget.CreateSoftwareBitmapAsync(sourceBitmap.BitmapPixelFormat); }
这个方案不仅代码更简洁,而且因为Win2D利用了硬件加速,性能比原生手动拷贝更高,适合对性能要求高的场景。
方案对比
- 纯原生API:无需额外依赖,适合不能引入第三方库的场景,但代码稍复杂,需要处理指针和内存拷贝;
- Win2D方案:代码简洁,性能更优,官方推荐的图形库,大多数UWP图形场景都适用。
内容的提问来源于stack exchange,提问作者Pawel
相关产品推荐
相关产品推荐

