Skip to content

Commit

Permalink
[Windows] Make WebView.CaptureAsync() work (#23474)
Browse files Browse the repository at this point in the history
  • Loading branch information
MartyIX authored Aug 19, 2024
1 parent 61e984e commit 5ff0613
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 19 deletions.
1 change: 1 addition & 0 deletions Microsoft.Maui-windows.slnf
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
"src\\TestUtils\\src\\DeviceTests\\TestUtils.DeviceTests.csproj",
"src\\TestUtils\\src\\Microsoft.Maui.IntegrationTests\\Microsoft.Maui.IntegrationTests.csproj",
"src\\TestUtils\\src\\TestUtils\\TestUtils.csproj",
"src\\TestUtils\\src\\UITest.Analyzers\\UITest.Analyzers.csproj",
"src\\TestUtils\\src\\UITest.Appium\\UITest.Appium.csproj",
"src\\TestUtils\\src\\UITest.Core\\UITest.Core.csproj",
"src\\TestUtils\\src\\UITest.NUnit\\UITest.NUnit.csproj",
Expand Down
34 changes: 34 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue14825.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Maui.Controls.Sample.Issues"
x:Class="Maui.Controls.Sample.Issues.Issue14825"
x:DataType="local:Issue14825">

<VerticalStackLayout>
<Label Text="WebView"/>
<WebView x:Name="myWebView" WidthRequest="400" HeightRequest="150">
<WebView.Source>
<HtmlWebViewSource>
<HtmlWebViewSource.Html>
<![CDATA[
<html>
<body>
<H1>.NET MAUI</H1>
<p>Welcome to WebView. 👍</p>
</body>
</html>
]]>
</HtmlWebViewSource.Html>
</HtmlWebViewSource>
</WebView.Source>
</WebView>

<VerticalStackLayout x:Name="screenshotResult" AutomationId="screenshotResult"/>

<Button AutomationId="Capture" Text="Capture" Clicked="CaptureButton_Clicked"/>

<Label AutomationId="TestInstructions" Margin="0,30,0,0" FontAttributes="Bold"
Text="Instructions: Click the capture button and expect a WebView screenshot to appear."/>
</VerticalStackLayout>
</ContentPage>
26 changes: 26 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue14825.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#nullable enable
namespace Maui.Controls.Sample.Issues;

[XamlCompilation(XamlCompilationOptions.Compile)]
[Issue(IssueTracker.Github, 14825, "Capture WebView screenshot", PlatformAffected.UWP)]
public partial class Issue14825 : ContentPage
{
public Issue14825()
{
InitializeComponent();
}

private async void CaptureButton_Clicked(object sender, EventArgs e)
{
IScreenshotResult? result = await myWebView.CaptureAsync();

if (result != null)
{
// Intentionally no "using" because ImageSource requires a valid stream.
Stream stream = await result.OpenReadAsync(ScreenshotFormat.Png, 100);

screenshotResult.Add(new Label() { Text = $"Your screenshot ({myWebView.Width}x{myWebView.Height}):" });
screenshotResult.Add(new Image() { Source = ImageSource.FromStream(() => stream), WidthRequest = myWebView.Width, HeightRequest = myWebView.Height });
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#if WINDOWS
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;

namespace Microsoft.Maui.TestCases.Tests.Issues;

public class Issue14825 : _IssuesUITest
{
public override string Issue => "Capture WebView screenshot";

public Issue14825(TestDevice device) : base(device)
{
}

[Test]
[Category(UITestCategories.WebView)]
public void ValidateWebViewScreenshot()
{
App.WaitForElement("TestInstructions");

// Click the capture button to capture a WebView screenshot.
App.Click("Capture");

VerifyScreenshot();
}
}
#endif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
61 changes: 42 additions & 19 deletions src/Essentials/src/Screenshot/Screenshot.uwp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,9 @@
using Windows.Graphics.Imaging;
using Windows.Storage.Streams;
using Microsoft.Maui.ApplicationModel;
#if WINDOWS
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media.Imaging;
#else
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media.Imaging;
#endif
using Microsoft.UI.Xaml.Controls;

namespace Microsoft.Maui.Media
{
Expand All @@ -32,32 +28,59 @@ public Task<IScreenshotResult> CaptureAsync(Window window) =>

public async Task<IScreenshotResult> CaptureAsync(UIElement element)
{
var bmp = new RenderTargetBitmap();
if (element is WebView2 webView)
{
InMemoryRandomAccessStream stream = new();
await webView.CoreWebView2.CapturePreviewAsync(Web.WebView2.Core.CoreWebView2CapturePreviewImageFormat.Png, stream);

BitmapDecoder decoder = await BitmapDecoder.CreateAsync(stream);
PixelDataProvider provider = await decoder.GetPixelDataAsync();
byte[] byteArray = provider.DetachPixelData();

// NOTE: Return to the main thread so we can access view properties such as
// width and height. Do not ConfigureAwait!
await bmp.RenderAsync(element);
return new ScreenshotResult((int)decoder.PixelWidth, (int)decoder.PixelHeight, byteArray, decoder.DpiX, decoder.DpiY);
}
else
{
var bmp = new RenderTargetBitmap();

// get the view information first
var width = bmp.PixelWidth;
var height = bmp.PixelHeight;
// NOTE: Return to the main thread so we can access view properties such as
// width and height. Do not ConfigureAwait!
await bmp.RenderAsync(element);

// then potentially move to a different thread
var pixels = await bmp.GetPixelsAsync().AsTask().ConfigureAwait(false);
// get the view information first
var width = bmp.PixelWidth;
var height = bmp.PixelHeight;

return new ScreenshotResult(width, height, pixels);
// then potentially move to a different thread
IBuffer pixels = await bmp.GetPixelsAsync().AsTask().ConfigureAwait(false);

return new ScreenshotResult(width, height, pixels);
}
}
}

partial class ScreenshotResult
{
readonly byte[] bytes;
readonly double _dpiX;
readonly double _dpiY;
readonly byte[] _bytes;

internal ScreenshotResult(int width, int height, byte[] bytes, double dpiX, double dpiY)
{
Width = width;
Height = height;
_bytes = bytes;
_dpiX = dpiX;
_dpiY = dpiY;
}

public ScreenshotResult(int width, int height, IBuffer pixels)
{
Width = width;
Height = height;
bytes = pixels?.ToArray() ?? throw new ArgumentNullException(nameof(pixels));
_bytes = pixels.ToArray() ?? throw new ArgumentNullException(nameof(pixels));
_dpiX = 96;
_dpiY = 96;
}

async Task<Stream> PlatformOpenReadAsync(ScreenshotFormat format, int quality)
Expand All @@ -74,14 +97,14 @@ Task PlatformCopyToAsync(Stream destination, ScreenshotFormat format, int qualit
}

Task<byte[]> PlatformToPixelBufferAsync() =>
Task.FromResult(bytes);
Task.FromResult(_bytes);

async Task EncodeAsync(ScreenshotFormat format, IRandomAccessStream ms)
{
var f = ToBitmapEncoder(format);

var encoder = await BitmapEncoder.CreateAsync(f, ms).AsTask().ConfigureAwait(false);
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, (uint)Width, (uint)Height, 96, 96, bytes);
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, (uint)Width, (uint)Height, _dpiX, _dpiY, _bytes);
await encoder.FlushAsync().AsTask().ConfigureAwait(false);
}

Expand Down

0 comments on commit 5ff0613

Please sign in to comment.