Search code examples
uwprendertargetbitmap

UWP - Use RenderTargetBitmap and only capture the visual area


I want to use RenderTargetBitmap to just capture the visual area in UWP app. But the result is to capture the full element image in canvas that include offscreen's part.

This is my application Image.

enter image description here

This my MainPage.xaml

<Page
x:Class="RenderBitmapTest.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:RenderBitmapTest"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Width="1600"
Height="900"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
Loaded="Page_Loaded"
mc:Ignorable="d">

<Grid>
    <Canvas
        x:Name="CanvasTest"
        Width="1600"
        Height="900"
        Background="Gray">
        <Rectangle
            Canvas.Left="-150"
            Canvas.Top="-150"
            Width="300"
            Height="300"
            Fill="Green" />
        <Rectangle
            Canvas.Left="0"
            Canvas.Top="150"
            Width="300"
            Height="300"
            Fill="Blue" />

        <Image x:Name="Image" Canvas.Left="300" />
    </Canvas>
</Grid>

This is RenderTargetBitmap Code.

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();
    }

    private async void Page_Loaded(object sender, RoutedEventArgs e)
    {
        CanvasTest.Measure(new Size(1600, 900));
        CanvasTest.Arrange(new Rect(0, 0, 1600, 900));
        var rtb = new RenderTargetBitmap();
        await rtb.RenderAsync(CanvasTest);

        await SaveToBitmapImage(rtb);
        //Image.Source = rtb;

    }

    private async Task SaveToBitmapImage(RenderTargetBitmap rtb)
    {
        StorageFolder storageFolder = ApplicationData.Current.LocalFolder;
        StorageFile storageFile = await storageFolder.CreateFileAsync("test.png", CreationCollisionOption.ReplaceExisting);

        var pixels = await rtb.GetPixelsAsync();
        using (IRandomAccessStream stream = await storageFile.OpenAsync(FileAccessMode.ReadWrite))
        {
            var encoder = await
            BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, stream);
            byte[] bytes = pixels.ToArray();
            encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore,
                (uint)rtb.PixelWidth, (uint)rtb.PixelHeight, 96, 96, bytes);
            await encoder.FlushAsync();
        }
    }

The result is enter image description here

The expectation should is to only capture the yellow area. enter image description here

Could anyone help to solve it?

Thanks, Zack


Solution

  • UWP - Use RenderTargetBitmap and only capture the visual area

    The problem is RenderTargetBitmap will render all the content area for Canvas container, so the better way is use Clip to set the RectangleGeometry and define the outline of the contents of a UIElement. Then RenderTargetBitmap the root Grid to replace CanvasTest

    <Grid  x:Name="RootGrid">
        <Canvas
        x:Name="CanvasTest"
        Background="Gray">
            <Canvas.Clip>
                <RectangleGeometry Rect="0,0,1600,900"/>
            </Canvas.Clip>
            <Rectangle
            Canvas.Left="-150"
            Canvas.Top="-150"
            Width="300"
            Height="300"
            Fill="Green" />
            <Rectangle
            Canvas.Left="0"
            Canvas.Top="150"
            Width="300"
            Height="300"
            Fill="Blue" />
    
            <Image x:Name="Image" Canvas.Left="300" />
        </Canvas>
    </Grid>
    

    Code Behind

    private async void Page_Loaded(object sender, RoutedEventArgs e)
    {
     
        var rtb = new RenderTargetBitmap();
        await rtb.RenderAsync(RootGrid);
        await SaveToBitmapImage(rtb);      
    
    }