I'm trying to encode a video from a series of screenshots of a UWP control. I can export still images with no issue, but when I try to convert a series of screenshots to a video the colours in the resulting video are either:
- brighter than those on screen when I use
DirectXPixelFormat.B8G8R8A8UIntNormalized
- dimmer than those on screen when I use
DirectXPixelFormat.B8G8R8A8UIntNormalizedSrgb
Using the code below.
How to I generate a video with the same colours as those on screen?
Bonus question: how to I reduce the number of steps to go from the UIElement
to the video?
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, 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, double>(async (info, status) =>
{
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, new DispatchedHandler(() =>
{
// Report success of failure
}));
});
}
EDIT1:
I think I've tracked the issue down to a possible bug in how Win2D canvases with bitmap images are rendered with RenderTargetBitmap
. In my main app the Win2D canvas is semi-transparent, so the brightness may have been from a white background that is handled differently on screen than by RenderTargetBitmap
.
Here is the issue described as simply as I could manange:
Check out the UWP app here. This app displays three buttons for exporting and blocks of colours rendered three different ways:
- As UWP Rectangle controls
- As Win2D Filled rectangles
- As a Win2D bitmap
I refer to these two images in the text below:
When no background is set on the parent control of the Win2D canvas (the StackPanel named Colours) it displays as image A on screen, but copies to the clipboard (using the button) as image B (where the white area is transparent).
If I set the background of the parent control to Black, it displays as image A on screen, and copies to the clipboard as image A.
If I set the background of the parent control to White, it displays as image B both on screen and also copies to the clipboard as image B.
I'm assuming this is a bug-or am I missing something?