Search code examples
c#xamluwpitemscontrolrendertargetbitmap

How to crop and save a RenderTargetBitmap UWP


I have an ItemsControl which contains a Canvas(800x800px) and a collection of Rectangles at certain coordinates. I can save the ItemsControlas a RenderTargetBitmap which is fine, but I need to crop it at a specified X,Y,W,H and then save it which I can't seem to figure out how to do.

I've tried clipping the Canvas using Canvas.Clip and then saving it afterwards but that makes my Rectangles move out of their specified coordinates (CollageX, CollageY), so the Canvas needs to be 800x800px.

EDIT: Or is there some way to Clip a canvas without it affecting the X and Y positions of its child elements?

Here's what my current code looks like.

C#

private async void SaveDesignBtn_Tapped(object sender, TappedRoutedEventArgs e)
    {

    //Images
    RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap();
    await renderTargetBitmap.RenderAsync(MaskArea, (int)PrintW, (int)PrintH);

    var pixelBuffer = await renderTargetBitmap.GetPixelsAsync();
    var pixels = pixelBuffer.ToArray();
    var displayInformation = DisplayInformation.GetForCurrentView();
    var file = await ApplicationData.Current.LocalFolder.CreateFileAsync("Canvas1" + ".png", CreationCollisionOption.ReplaceExisting);
    using (var stream = await file.OpenAsync(FileAccessMode.ReadWrite))
        {
            var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
            encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied, (uint)renderTargetBitmap.PixelWidth, (uint)renderTargetBitmap.PixelHeight, displayInformation.RawDpiX, displayInformation.RawDpiY, pixels);
            await encoder.FlushAsync();
        }
    }

XAML

    <ItemsControl Name="MaskArea" ItemsSource="{Binding Path=CollageGrid}" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,0,0,0" Width="800" Height="800">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas x:Name="CollageArea" 
                        Background="Transparent" 
                        Width="800" 
                        Height="800"                
                        HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,0,0,0">
                </Canvas>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate x:DataType="local:CollageGrid">
                <Rectangle Name="MaskBounds" Width="{Binding CollageW}" Height="{Binding CollageH}" AllowDrop="True" CanDrag="True"
                            Drop="Mask_Drop"  
                            DragOver="Mask_DragOver"
                            ManipulationMode="All" Stroke="Black" StrokeThickness="2" DragEnter="Mask_DragEnter" DragLeave="Mask_DragLeave" Tapped="Tap_Collage"
                            RenderTransformOrigin="0.5, 0.5"
                            Canvas.Left="{Binding CollageX}" Canvas.Top="{Binding CollageY}" Fill="Transparent">
                        <Rectangle.RenderTransform>
                            <TranslateTransform X="{Binding CollageX}" Y="{Binding CollageY}"/>
                        </Rectangle.RenderTransform>
                </Rectangle>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>

Solution

  • Don't clip it before you save: clip it when you save.

    The BitmapEncoder has a BitmapTransform property which you can use to control how it is encoded, including scaling, flipping, rotating, and clipping.