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:
- to select an image;
- to draw over said image;
- 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.