问题描述
我正在尝试从 UWP 控件的一系列屏幕截图中编码视频。我可以使用 no issue 导出静止图像,但是当我尝试将一系列屏幕截图转换为视频时,生成的视频中的颜色是:
- 使用
DirectXPixelFormat.B8G8R8A8UIntnormalized
时比屏幕上的更亮 - 当我使用
DirectXPixelFormat.B8G8R8A8UIntnormalizedSrgb
时比屏幕上的更暗
使用下面的代码。
如何生成与屏幕颜色相同的视频?
额外问题:如何减少从 UIElement
到视频的步骤数?
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)
{
// Code here to modify the "content" control
rendertargetBitmap rendertargetBitmap = new();
await rendertargetBitmap.RenderAsync(content);
Canvasrendertarget rendertarget = null;
using (CanvasBitmap canvas = CanvasBitmap.CreateFromBytes(
CanvasDevice.GetSharedDevice(),await rendertargetBitmap.GetPixelsAsync(),rendertargetBitmap.PixelWidth,rendertargetBitmap.PixelHeight,// Pixel format specified here:
DirectXPixelFormat.B8G8R8A8UIntnormalized))
{
rendertarget = new Canvasrendertarget(CanvasDevice.GetSharedDevice(),width,height,96);
using CanvasDrawingSession ds = rendertarget.CreateDrawingSession();
ds.Clear(Colors.White);
ds.DrawImage(canvas,0);
}
MediaClip clip = MediaClip.CreateFromSurface(rendertarget,frameTime);
composition.Clips.Add(clip);
}
var profile = MediaEncodingProfile.CreateMp4(VideoEncodingQuality.HD1080p);
// Avoid odd video dimensions which some encoders don't like
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(() =>
{
// Report progress
}));
});
saveOperation.Completed = new AsyncoperationWithProgressCompletedHandler<TranscodeFailureReason,status) =>
{
await CoreApplication.MainView.CoreWindow.dispatcher.RunAsync(CoredispatcherPriority.normal,new dispatchedHandler(() =>
{
// Report success of failure
}));
});
}
编辑 1:
我认为我已经将问题归结为一个可能的错误,即带有位图图像的 Win2D 画布如何使用 rendertargetBitmap
呈现。在我的主应用中,Win2D 画布是半透明的,因此亮度可能来自白色背景,该背景在屏幕上的处理方式与 rendertargetBitmap
不同。
这是我可以管理的简单描述的问题:
查看 UWP 应用 here。 此应用显示三个用于导出的按钮和以三种不同方式呈现的颜色块:
- 作为 UWP 矩形控件
- 作为 Win2D 填充矩形
- 作为 Win2D 位图
当 Win2D 画布的父控件(名为 Colours 的 StackPanel)上没有设置背景时,它在屏幕上显示为图像 A,但复制到剪贴板(使用按钮)作为图像 B(其中白色区域是透明的) ).
如果我将父控件的背景设置为黑色,它在屏幕上显示为图像 A,并作为图像 A 复制到剪贴板。
如果我将父控件的背景设置为白色,它会在屏幕上显示为图像 B,并作为图像 B 复制到剪贴板。
解决方法
解决方案是使用 255 作为 alpha:
var colours = new byte[]
{
255,255,255
};
这是因为 CanvasBitmap.CreateFromBytes 使用 预乘 alpha,如预乘 alpha 中所述。
预乘 Alpha 混合
以下是思考透明度的不同方式:
RGB 指定对象对场景贡献多少颜色 Alpha 指定它遮蔽它背后的东西的程度 数学:
blend(source,dest) = source.rgb + (dest.rgb * (1 - source.a))
在代码中:
RenderState.SourceBlend = Blend.One;
RenderState.DestinationBlend = Blend.InverseSourceAlpha;
在这个世界里,RGB 和 alpha 是相互关联的。使对象透明。
来自元数据的函数定义:
//
// Summary:
// Creates a CanvasBitmap from an array of bytes,using the specified pixel width/height,// premultiplied alpha and default (96) DPI.
[DefaultOverload]
[Overload("CreateFromBytes")]
public static CanvasBitmap CreateFromBytes(ICanvasResourceCreator resourceCreator,byte[] bytes,int widthInPixels,int heightInPixels,DirectXPixelFormat format);
代码:
var colours = new byte[]
{
255,0
};
CanvasBitmap bitmap = CanvasBitmap.CreateFromBytes(args.DrawingSession,colours,1,3,DirectXPixelFormat.R8G8B8A8UIntNormalized);
args.DrawingSession.DrawImage(bitmap,new Rect(0,150,200,150));
由于每种颜色的 alpha 值为 0,根据上面的公式,结果是 source.rgb + dest.rgb。如果 dest.rgb 为白色 (255,255),则为 (255,255)。如果 dest.rgb 为黑色(0,0),则为 source.rgb。如果 dest.rgb 为 Blue (0,255),则为 (source.r,source.g,255),依此类推。
当每种颜色的 alpha 值为 255 时,根据公式,无论 dest.rgb 是什么,结果都是 source.rgb。