屏幕截图视频颜色比屏幕上的颜色更亮或更暗

问题描述

我正在尝试从 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 位图

我在下面的文字中提到了这两张图片

A

enter image description here

enter image description here

当 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。