Adam McMahon
Adam McMahon

Reputation: 800

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>

Upvotes: 1

Views: 1244

Answers (2)

Xeorge Xeorge
Xeorge Xeorge

Reputation: 462

ok, so i am not sure if this helps, but people looking up how to crop an image will sure enjoy this answer as its really clean and fast.

private async void GetCanvasBitmapRegion(Rect RegionToCopy)
        {
            try
            {
                CanvasDevice Cdevice = CanvasDevice.GetSharedDevice();



                var croppedwidth = (int)RegionToCopy.Width;
                var croppedheight = (int)RegionToCopy.Height;

                //create a new empty image that has the same size as the desired crop region
                var softwareBitmap = new SoftwareBitmap(BitmapPixelFormat.Bgra8, croppedwidth, croppedheight,
                    BitmapAlphaMode.Premultiplied);

                //based on this empty software bitmap we create a new CanvasBitmap
                var croppedimage = CanvasBitmap.CreateFromSoftwareBitmap(Cdevice, softwareBitmap);

                // this is the image we want to crop from, CanvasBitmap has lots of static initializers that like CanvasBitmap.LoadAsync 
                //or CanvasBitmap.CreateFromBytes
                CanvasBitmap initialimage = _image;

                if (initialimage != null)
                {

                    //this function does the cropped region copy.
                    croppedimage.CopyPixelsFromBitmap(_image, 0, 0, (int)RegionToCopy.Left, (int)RegionToCopy.Top, (int)RegionToCopy.Width, (int)RegionToCopy.Height);


                   //you can now do whatever you like with croppedimage, including using its .SaveAsync or replace the old one with it.
                }

            }
            catch (Exception Ex)
            {

            }
        }

Upvotes: 0

Rob Caplan - MSFT
Rob Caplan - MSFT

Reputation: 21919

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.

Upvotes: 1

Related Questions