UWP drawing over an image and saving to DB

244 Views Asked by At

First of all, a disclaimer: I'm pretty new to UWP and C# (I've used C and C++ in the past, but most of my coding experience is in Java, and it has been a few years since I've done any coding with regularity). That said, I'm trying to develop a simple app that will allow for the user:

  1. to select an image;
  2. to draw over said image;
  3. to save it to the database.

I've gather some examples here and there and have now a quasi-working example: I can load the image from the filesystem, can draw over it and save it to the DB, however the image is not in the correct scale - I'm getting only a small part of the original image, as if only the upper left corner of the image was cropped.

This is the code. My view:

<Page
    x:Class="CadastroPacientes.Views.FotoView"
    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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Grid>
        <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
            <Grid>
                <Image x:Name="image" Width="600" Height="600"/>
                <InkCanvas x:Name="ink" Width="600" Height="600"/>
            </Grid>
            <InkToolbar TargetInkCanvas="{x:Bind ink}"/>
            <StackPanel Orientation="Horizontal">
                <Button 
                    Content="Escolher imagem"
                    VerticalAlignment="Top" 
                    Margin="10,10,0,0"
                    Click="EscolherImagem_Click"/>
                <Button 
                    Content="Salvar"
                    Margin="10,10,0,0"
                    VerticalAlignment="Top" 
                    Click="SalvarImagem_Click"/>
            </StackPanel>
        </StackPanel>
    </Grid>
</Page>

And the code behind:

using System;
using System.Collections.Generic;
using Windows.UI.Xaml.Controls;
using Microsoft.Graphics.Canvas;
using Windows.Foundation;
using Windows.Storage;
using Windows.Storage.Pickers;
using Windows.Storage.Streams;
using Windows.UI;
using Windows.UI.Input.Inking;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media.Imaging;
using Windows.UI.Xaml.Navigation;
using Windows.UI.Core;
using CadastroPacientes.Modelo;
using CadastroPacientes.ViewModel;
using System.IO;
using System.Threading.Tasks;
using Windows.Graphics.Imaging;
using CadastroPacientes.Converters;

namespace CadastroPacientes.Views
{
    sealed partial class FotoView : Page
    {
        FotoViewModel ViewModel = new FotoViewModel();

        StorageFile InputFile;
        
        BitmapImage bitmapImage;

        SoftwareBitmap softBM;

        public FotoView()
        {

            this.InitializeComponent();
            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 EscolherImagem_Click(object sender, RoutedEventArgs e)
        {
            var picker = new FileOpenPicker();
            picker.ViewMode = PickerViewMode.Thumbnail;
            picker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
            picker.FileTypeFilter.Add(".jpg");
            InputFile = await picker.PickSingleFileAsync();
            if(InputFile == null)
            {
                //the user cancelled the operation
                return;
            }

            // Ensure the stream is disposed once the image is loaded
            using (IRandomAccessStream fileStream = await InputFile.OpenAsync(Windows.Storage.FileAccessMode.Read))
            {
                // Create the decoder from the stream
                BitmapDecoder decoder = await BitmapDecoder.CreateAsync(fileStream);

                // Get the SoftwareBitmap representation of the file
                softBM = await decoder.GetSoftwareBitmapAsync();

                if (softBM.BitmapPixelFormat != BitmapPixelFormat.Bgra8 ||
                    softBM.BitmapAlphaMode == BitmapAlphaMode.Straight)
                {
                    softBM = SoftwareBitmap.Convert(softBM, BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);
                }



                var source = new SoftwareBitmapSource();

                await source.SetBitmapAsync(softBM);



                // Set the source of the Image control
                image.Source = source;
            }

        }

        private async void SalvarImagem_Click(object sender, RoutedEventArgs e)
        {

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

            using (var ds = renderTarget.CreateDrawingSession())
            {
                ds.Clear(Colors.White);
                ds.DrawImage(CanvasBitmap.CreateFromSoftwareBitmap(ds, softBM));
                // then draw contents of your ink canvas over it
                ds.DrawInk(ink.InkPresenter.StrokeContainer.GetStrokes());
            }

            // save results
            IRandomAccessStream stream = new InMemoryRandomAccessStream(); 
            await renderTarget.SaveAsync(stream, CanvasBitmapFileFormat.Jpeg);
            var readStream = stream.AsStreamForRead();
            var byteArray = new byte[readStream.Length];
            readStream.Read(byteArray, 0, byteArray.Length);
            ViewModel.salvarFoto(byteArray, InputFile.Name);
           
            this.Frame.Navigate(typeof(ConsultaView), this.ViewModel.Consulta);
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            if (e.Parameter != null && e.Parameter is ConsultaViewModel)
            {
                ConsultaViewModel cvm = (ConsultaViewModel)e.Parameter;
                this.ViewModel.Consulta = cvm.Model;
                this.ViewModel.foto = cvm.SelectedFoto;

                if(this.ViewModel.foto != null)
                {
                    this.LoadImage();
                }
            }
        }

        private async void LoadImage()
        {
            //image selected for edition
            ByteToBitmapImageConverter converter = new ByteToBitmapImageConverter();
            BitmapImage selectedImage = await converter.ConvertByteToImage(this.ViewModel.foto.Image);
            // Set the source of the Image control
            image.Source = selectedImage;
        }
    }
}

I reckon I need something like bitmapImage.DecodePixelHeight = (int)ink.Height to the SoftwareBitmap object, but I don't know how to do it. Otherwise, if I could draw a BitmapImage directly into the DrawingSession, this would also resolve it. Hopefully I've made myself clear. Thanks in advance for any help.

0

There are 0 best solutions below