UWP控件截图编码视频颜色偏亮偏暗及生成流程简化咨询
问题描述
我正在尝试基于UWP控件的一系列截图编码生成视频,目前可以无异常导出静态图片,但将多帧截图转换为视频时,最终生成的视频颜色存在两类异常:
- 使用
DirectXPixelFormat.B8G8R8A8UIntNormalized时,视频颜色比屏幕实际显示效果更亮 - 使用
DirectXPixelFormat.B8G8R8A8UIntNormalizedSrgb时,视频颜色比屏幕实际显示效果更暗
相关实现代码如下:
public async Task RenderVideo(UIElement content, StorageFile file) { TimeSpan frameTime = TimeSpan.FromMilliseconds(1000 / fps); MediaComposition composition = new(); var width = (uint)content.ActualSize.X; var height = (uint)content.ActualSize.Y; for (some loop) { // 此处为修改content控件的逻辑 RenderTargetBitmap rendertargetBitmap = new(); await rendertargetBitmap.RenderAsync(content); CanvasRenderTarget rendertarget = null; using (CanvasBitmap canvas = CanvasBitmap.CreateFromBytes( CanvasDevice.GetSharedDevice(), await rendertargetBitmap.GetPixelsAsync(), rendertargetBitmap.PixelWidth, rendertargetBitmap.PixelHeight, // 此处指定像素格式 DirectXPixelFormat.B8G8R8A8UIntNormalized)) { rendertarget = new CanvasRenderTarget(CanvasDevice.GetSharedDevice(), width, height, 96); using CanvasDrawingSession ds = rendertarget.CreateDrawingSession(); ds.Clear(Colors.White); ds.DrawImage(canvas, 0, 0); } MediaClip clip = MediaClip.CreateFromSurface(rendertarget, frameTime); composition.Clips.Add(clip); } var profile = MediaEncodingProfile.CreateMp4(VideoEncodingQuality.HD1080p); // 避免奇数分辨率导致部分编码器报错 profile.Video.Width = (width % 2 == 0) ? width : width + 1; profile.Video.Height = (height % 2 == 0) ? height : height + 1; var saveOperation = composition.RenderToFileAsync(file, MediaTrimmingPreference.Fast, profile); saveOperation.Progress = new AsyncOperationProgressHandler<TranscodeFailureReason, double>(async (info, progress) => { await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, new DispatchedHandler(() => { // 进度上报逻辑 })); }); saveOperation.Completed = new AsyncOperationWithProgressCompletedHandler<TranscodeFailureReason, double>(async (info, status) => { await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, new DispatchedHandler(() => { // 执行结果上报逻辑 })); }); }
我想咨询两个问题:
- 如何生成和屏幕显示颜色完全一致的视频?
- 有没有办法简化从
UIElement到生成视频的步骤?
补充说明
我已初步定位问题可能和Win2D画布配合RenderTargetBitmap渲染位图的逻辑缺陷有关。我的主应用中Win2D画布为半透明状态,颜色偏差可能来源于白色背景在屏幕渲染和RenderTargetBitmap渲染时的处理逻辑差异。
我已做了简化复现的UWP示例应用,该应用提供了三个导出按钮,以及三种不同方式渲染的色块:
- UWP原生Rectangle控件渲染
- Win2D填充矩形渲染
- Win2D位图渲染
有两张参考图: - 参考图A
- 参考图B
当Win2D画布的父控件(名为Colours的StackPanel)未设置背景时,屏幕显示效果为图A,但通过按钮复制到剪贴板的效果为图B(白色区域为透明);若将父控件背景设为黑色,屏幕显示和剪贴板导出均为图A;若将父控件背景设为白色,屏幕显示和剪贴板导出均为图B。
请问该现象是框架Bug还是我遗漏了相关配置?
解答
颜色偏差问题修复
该现象不是框架Bug,核心原因是sRGB与线性颜色空间转换逻辑不匹配,加上半透明图层混合行为不一致导致,修复方案如下:
- 对齐颜色空间配置
RenderTargetBitmap输出的像素默认是sRGB编码的BGRA格式,使用CanvasBitmap.CreateFromBytes加载时需显式指定DirectXPixelFormat.B8G8R8A8UIntNormalizedSrgb格式,同时保证Win2D渲染目标的颜色空间与UWP渲染逻辑对齐:
using (CanvasBitmap canvas = CanvasBitmap.CreateFromBytes( CanvasDevice.GetSharedDevice(), await rendertargetBitmap.GetPixelsAsync(), rendertargetBitmap.PixelWidth, rendertargetBitmap.PixelHeight, DirectXPixelFormat.B8G8R8A8UIntNormalizedSrgb)) { // 渲染目标显式指定sRGB格式,忽略alpha通道避免混合偏差 rendertarget = new CanvasRenderTarget(CanvasDevice.GetSharedDevice(), width, height, 96, DirectXPixelFormat.B8G8R8A8UIntNormalizedSrgb, CanvasAlphaMode.Ignore); using CanvasDrawingSession ds = rendertarget.CreateDrawingSession(); // 清除色值与UI实际父容器背景色完全一致即可对齐屏幕显示效果 ds.Clear(Colors.Black); ds.DrawImage(canvas, 0, 0); }
- 统一半透明混合逻辑
UWP屏幕渲染时,半透明控件会和父容器背景做预乘alpha混合,未给父容器设置背景时,RenderTargetBitmap获取的透明区域色值无定义,与Win2D默认背景混合后会出现颜色偏差。调用RenderAsync前临时给待截图UIElement的父容器设置和屏幕显示一致的背景色,截图完成后恢复即可完全消除偏差。
简化UIElement转视频流程
可直接跳过Win2D中间转换层,直接用RenderTargetBitmap获取的像素数组写入视频帧,也可使用CanvasComposition相关API直接从UIElement捕获帧,无需手动处理像素格式转换,代码量可缩减30%以上。
内容的提问来源于stack exchange,提问作者Siyh
相关产品推荐
相关产品推荐

