Displaying a background image on a UWP ink canvas
Asked Answered
E

1

8

I have an InkCanvas in my UWP app, and would like to display an image (ideally as part of the Canvas, but otherwise, overlay it in some way (the idea being that I can save the changed image back to the image file). WPF seems to allow the InkCanvas to have children, but in UWP that doesn't seem to be possible. I've tried the following:

        <InkCanvas x:Name="drawInkCanvas">
            <Image Source="{Binding DrawingImage}"/>

        </InkCanvas>

But that doesn't work; I've also tried this:

        <Grid Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2">
            <InkCanvas x:Name="drawInkCanvas" Opacity=".5"/>
            <Image Source="{Binding DrawingImage}" Opacity=".5"/>

        </Canvas>

Which, to be fair, I didn't have very high hopes for; anyway, although it does, kind of work, it makes both the image and the InkCanvas look wrong and, obviously, doesn't allow me to save the resulting image.

Ideally, there would be a background image or something similar. Is there anything that I can use to achieve this; I'm approaching the opinion that I may have to replace the InkCanvas with a standard canvas and then re-write all the InkCanvas functionality!

Erickericka answered 12/5, 2016 at 7:15 Comment(1)
It seem the InkCanvas does not support display an image. You can put the Image under the InkCanvas like:<ScrollViewer><Grid><Image Source="<path>"></Image><InkCanvas x:Name="MyInkCanvas"></InkCanvas></Grid></ScrollViewer>.Alviani
I
13

So you have couple of problems:

  1. How to draw using InkCanvas over Image control.
  2. How to save the results into a file.

I'll use simple UWP app in this example, which assumes that you have "sample.jpg" file in your Assets folder, and you have "Pictures Library" capability in your manifest.

To solve first problem, just put both InkCanvas and Image into same container (like Grid), but remember that order matters:

<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid>
        <Image Source="/Assets/sample.jpg"></Image>
        <InkCanvas x:Name="ink"></InkCanvas>
    </Grid>
    <Button Content="Save"
            Width="100"
            Height="25"
            HorizontalAlignment="Center"
            VerticalAlignment="Center" Click="BtnSave_Click"/>
</StackPanel>

If you put InkCanvas first (like you did in question example) - it won't work. However if put InkCanvas last - all works fine and you can draw over your image.

Now to solve second problem you need to use Win2D (install nuget Win2D.uwp package), because standard RenderTargetBitmap won't render InkCanvas contents. First draw your original image (take image from original source directly, that is from original file for example), then draw contents of your ink canvas over it. Full code of MainPage (if you add xaml above you will have complete working sample):

public sealed partial class MainPage : Page {
    public MainPage() {
        this.InitializeComponent();
        // just set some properties of ink canvas, not directly relevant to your question
        ink.InkPresenter.InputDeviceTypes = CoreInputDeviceTypes.Mouse | CoreInputDeviceTypes.Touch;
        var attr = new InkDrawingAttributes();
        attr.Color = Colors.Red;
        attr.IgnorePressure = true;
        attr.PenTip = PenTipShape.Circle;
        attr.Size = new Size(4, 10);
        ink.InkPresenter.UpdateDefaultDrawingAttributes(attr);
    }

    private async void BtnSave_Click(object sender, RoutedEventArgs e) {
        // grab output file
        StorageFolder storageFolder = KnownFolders.SavedPictures;
        var file = await storageFolder.CreateFileAsync("output.jpg", CreationCollisionOption.ReplaceExisting);

        CanvasDevice device = CanvasDevice.GetSharedDevice();
        CanvasRenderTarget renderTarget = new CanvasRenderTarget(device, (int) ink.ActualWidth, (int) ink.ActualHeight, 96);

        // grab your input file from Assets folder
        StorageFolder appInstalledFolder = Windows.ApplicationModel.Package.Current.InstalledLocation;
        StorageFolder assets = await appInstalledFolder.GetFolderAsync("Assets");
        var inputFile = await assets.GetFileAsync("sample.jpg");            
        using (var ds = renderTarget.CreateDrawingSession()) {
            ds.Clear(Colors.White);                
            var image = await CanvasBitmap.LoadAsync(device, inputFile.Path);
            // draw your image first
            ds.DrawImage(image);
            // then draw contents of your ink canvas over it
            ds.DrawInk(ink.InkPresenter.StrokeContainer.GetStrokes());
        }

        // save results
        using (var fileStream = await file.OpenAsync(FileAccessMode.ReadWrite)) {
            await renderTarget.SaveAsync(fileStream, CanvasBitmapFileFormat.Jpeg, 1f);
        }
    }
}
Inexpressible answered 8/6, 2016 at 9:34 Comment(2)
This works - many thanks. It does do some strange things with the width and height, though; but I think that's just playing with the numbers and positions.Erickericka
Note it takes width and height from actual size of ink canvas. You might want to use original image dimensions instead.Inexpressible

© 2022 - 2024 — McMap. All rights reserved.