[英]how to save work done on canvas using InkCanvas to an image file in UWP C#?
I want to save my work done on a canvas in my UWP app.我想在我的 UWP 应用程序中保存我在 canvas 上完成的工作。 I am using InkCanvas to draw lines on an selected image inside the canvas and I want to save the canvas work to a new image file.
我正在使用 InkCanvas 在 canvas 内的选定图像上绘制线条,并且我想将 canvas 工作保存到新的图像文件中。
I am getting a blank image after trying to save the file.尝试保存文件后,我得到一个空白图像。 I've tried two approaches to save file.
我尝试了两种保存文件的方法。
work done:完成工作:
xaml code xaml代码
<Button Click="ShowPopup" Content="click me"/>
<Popup x:Name="IMG_G" Width="600" Height="300" HorizontalAlignment="Left" ManipulationMode="All">
<Grid x:Name="img_grid" Height="300" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" ManipulationMode="Scale">
<Image VerticalAlignment="Top" HorizontalAlignment="Left"
x:Name="img" Stretch="Fill" Height="300" ManipulationMode="All">
</Image>
<Canvas x:Name="selectionCanvas" Width="600" Background="Transparent" Height="300"/>
<InkCanvas x:Name="inker" />
<InkToolbar x:Name="img_inktoolbar" TargetInkCanvas="{x:Bind inker}"
VerticalAlignment="Top">
</InkToolbar>
</Grid>
</Popup>
<Button Content="Save"
Width="100"
Height="25"
HorizontalAlignment="Center"
VerticalAlignment="Center" Click="BtnSave_Click"/>
Code Behind代码背后
public DrawLines()
{
InitializeComponent();
inker.InkPresenter.InputDeviceTypes = CoreInputDeviceTypes.Mouse | CoreInputDeviceTypes.Touch;
inker.InkPresenter.UnprocessedInput.PointerPressed += StartLine;
inker.InkPresenter.UnprocessedInput.PointerMoved += ContinueLine;
inker.InkPresenter.UnprocessedInput.PointerReleased += CompleteLine;
inker.InkPresenter.InputProcessingConfiguration.RightDragAction = InkInputRightDragAction.LeaveUnprocessed;
}
private async void ShowPopup(object sender, RoutedEventArgs e)
{
var _filePicker = new FileOpenPicker();
_filePicker.SuggestedStartLocation = PickerLocationId.Desktop;
_filePicker.ViewMode = PickerViewMode.Thumbnail;
_filePicker.FileTypeFilter.Add(".bmp");
_filePicker.FileTypeFilter.Add(".jpg");
StorageFile _file = await _filePicker.PickSingleFileAsync();
IRandomAccessStream imageStream = await _file.OpenAsync(FileAccessMode.Read);
BitmapImage bmpimage = new BitmapImage();
await bmpimage.SetSourceAsync(imageStream);
img.Source = bmpimage;
IMG_G.IsOpen = true;
}
private void StartLine(InkUnprocessedInput sender, PointerEventArgs args)
{
line = new Line();
line.X1 = args.CurrentPoint.RawPosition.X;
line.Y1 = args.CurrentPoint.RawPosition.Y;
line.X2 = args.CurrentPoint.RawPosition.X;
line.Y2 = args.CurrentPoint.RawPosition.Y;
line.Stroke = new SolidColorBrush(Colors.Purple);
line.StrokeThickness = 4;
selectionCanvas.Children.Add(line);
}
private void ContinueLine(InkUnprocessedInput sender, PointerEventArgs args)
{
line.X2 = args.CurrentPoint.RawPosition.X;
line.Y2 = args.CurrentPoint.RawPosition.Y;
}
private void CompleteLine(InkUnprocessedInput sender, PointerEventArgs args)
{
List<InkPoint> points = new List<InkPoint>();
InkStrokeBuilder builder = new InkStrokeBuilder();
InkPoint pointOne = new InkPoint(new Point(line.X1, line.Y1), 0.5f);
points.Add(pointOne);
InkPoint pointTwo = new InkPoint(new Point(line.X2, line.Y2), 0.5f);
points.Add(pointTwo);
InkStroke stroke = builder.CreateStrokeFromInkPoints(points, System.Numerics.Matrix3x2.Identity);
InkDrawingAttributes ida = inker.InkPresenter.CopyDefaultDrawingAttributes();
stroke.DrawingAttributes = ida;
inker.InkPresenter.StrokeContainer.AddStroke(stroke);
selectionCanvas.Children.Remove(line);
}
Approach 1 for saving the file保存文件的方法1
private async void BtnSave_Click(object sender, RoutedEventArgs e)
{
StorageFolder pictureFolder = KnownFolders.SavedPictures;
var file = await pictureFolder.CreateFileAsync("test2.bmp", CreationCollisionOption.ReplaceExisting);
CanvasDevice device = CanvasDevice.GetSharedDevice();
CanvasRenderTarget renderTarget = new CanvasRenderTarget(device, (int)img.ActualWidth, (int)img.ActualHeight, 96);
//get image's path
StorageFolder folder = ApplicationData.Current.LocalFolder;
//Get the same image file copy which i selected to draw on in ShowPopup() but I actually wanted to get the edited canvas
StorageFile Ifile = await folder.GetFileAsync("Datalog_2020_09_22_10_44_59_2_3_5_RSF.bmp");
var inputFile = Ifile.Path;
using (var ds = renderTarget.CreateDrawingSession())
{
ds.Clear(Colors.White);
CanvasBitmap image = await CanvasBitmap.LoadAsync(device, inputFile);
//var image = img2.Source;
// I want to use this too, but I have no idea about this
ds.DrawImage(image);
ds.DrawInk(inker.InkPresenter.StrokeContainer.GetStrokes());
}
// save results
using (var fileStream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
await renderTarget.SaveAsync(fileStream, CanvasBitmapFileFormat.Jpeg, 1f);
}
}
Results结果
desired result期望的结果
Note: Arrow is drawn by me on the image注意:箭头是我在图片上绘制的
Result I am getting with this approach结果我用这种方法得到
test2.bmp (image is getting zoomed in for some reason) test2.bmp(图像由于某种原因被放大)
Approach 2 for saving the file保存文件的方法2
private async void BtnSave_Click(object sender, RoutedEventArgs e)
{
RenderTargetBitmap bitmap = new RenderTargetBitmap();
await bitmap.RenderAsync(selectionCanvas);
Debug.WriteLine($"Capacity = {(uint)bitmap.PixelWidth}, Length={(uint)bitmap.PixelHeight}");
var pixelBuffer = await bitmap.GetPixelsAsync();
Debug.WriteLine($"Capacity = {pixelBuffer.Capacity}, Length={pixelBuffer.Length}");
byte[] pixels = pixelBuffer.ToArray();
var displayInformation = DisplayInformation.GetForCurrentView();
StorageFolder pictureFolder = KnownFolders.SavedPictures;
var file = await pictureFolder.CreateFileAsync("test2.bmp", CreationCollisionOption.ReplaceExisting);
using (var stream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.BmpEncoderId, stream);
encoder.SetPixelData(BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Ignore,
(uint)bitmap.PixelWidth,
(uint)bitmap.PixelHeight,
displayInformation.RawDpiX,
displayInformation.RawDpiY,
pixels);
await encoder.FlushAsync();
}
}
Result with this approach这种方法的结果
For some reason I am getting all black image出于某种原因,我得到全黑图像
test2.bmp测试2.bmp
Any help will be apppreciated.任何帮助将不胜感激。 Any help with approach 2 will be better.
方法2的任何帮助都会更好。
how to save work done on canvas using InkCanvas to an image file in UWP C#?
如何使用 InkCanvas 将 canvas 上完成的工作保存到 UWP C# 中的图像文件中?
You have no need use RenderTargetBitmap
to save InkCanvas to image.您无需使用
RenderTargetBitmap
将 InkCanvas 保存到图像。 UWP InkCanvas
has SaveAsync method that could save the StrokeContainer
stream to image file directly. UWP
InkCanvas
具有 SaveAsync 方法,可以将StrokeContainer
stream 直接保存到图像文件中。 For example.例如。
async void OnSaveAsync(object sender, RoutedEventArgs e)
{
// We don't want to save an empty file
if (inkCanvas.InkPresenter.StrokeContainer.GetStrokes().Count > 0)
{
var savePicker = new FileSavePicker();
savePicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
savePicker.FileTypeChoices.Add("png with embedded ISF", new[] { ".png" });
StorageFile file = await savePicker.PickSaveFileAsync();
if (null != file)
{
try
{
using (IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
// Truncate any existing stream in case the new file
// is smaller than the old file.
stream.Size = 0;
await inkCanvas.InkPresenter.StrokeContainer.SaveAsync(stream);
}
}
catch (Exception ex)
{
}
}
}
else
{
}
}
For more detail please refer UWP simple ink code sample scenario 3更多详情请参考 UWP 简单墨水代码示例场景3
Update更新
I am getting a blank image after trying to save the file.
尝试保存文件后,我得到一个空白图像。
Above code can only save InkCanvas
stroke, I checked your code, I found you have not place any element in the selectionCanvas
.上面的代码只能保存
InkCanvas
笔画,我检查了你的代码,我发现你没有在selectionCanvas
中放置任何元素。 so the RenderTargetBitmap
of selectionCanvas
will black empty.所以
selectionCanvas
的RenderTargetBitmap
将变黑为空。 Please try to use img_grid
to replace.请尝试使用
img_grid
替换。
Hey sorry but now the InkToolbar is also getting copied on the image along with the ink changes i make:(
嘿抱歉,但现在 InkToolbar 也与我所做的墨水更改一起被复制到图像上:(
It's by-design, RenderTargetBitmap
will render all element that was viewed, for your scenario, we suggest you make rectangle to covered InkToolbar
or set img_inktoolbar Visibility as Collapsed, before you capture the screen and reset it after finish.这是设计使然,
RenderTargetBitmap
将渲染所有查看过的元素,对于您的场景,我们建议您制作矩形覆盖InkToolbar
或将 img_inktoolbar Visibility 设置为 Collapsed,然后再捕获屏幕并在完成后重置它。
I made some changes in approach 2 and got the desired result我对方法 2 进行了一些更改并得到了预期的结果
Approach 2 for saving the file保存文件的方法2
private async void BtnSave_Click(object sender, RoutedEventArgs e)
{
// In order to hide the InkToolbar before the saving the image
img_inktoolbar.Visibility = Visibility.Collapsed;
RenderTargetBitmap bitmap = new RenderTargetBitmap();
await bitmap.RenderAsync(img_grid);
Debug.WriteLine($"Capacity = {(uint)bitmap.PixelWidth}, Length={(uint)bitmap.PixelHeight}");
var pixelBuffer = await bitmap.GetPixelsAsync();
Debug.WriteLine($"Capacity = {pixelBuffer.Capacity}, Length={pixelBuffer.Length}");
byte[] pixels = pixelBuffer.ToArray();
var displayInformation = DisplayInformation.GetForCurrentView();
StorageFolder pictureFolder = KnownFolders.SavedPictures;
var file = await pictureFolder.CreateFileAsync("test2.bmp", CreationCollisionOption.ReplaceExisting);
using (var stream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.BmpEncoderId, stream);
encoder.SetPixelData(BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Ignore,
(uint)bitmap.PixelWidth,
(uint)bitmap.PixelHeight,
displayInformation.RawDpiX,
displayInformation.RawDpiY,
pixels);
await encoder.FlushAsync();
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.