如何将使用 InkCanvas 在 canvas 上完成的工作保存到 UWP C# 中的图像文件?
how to save work done on canvas using InkCanvas to an image file in UWP C#?
我想在我的 UWP 应用程序中保存在 canvas 上完成的工作。我正在使用 InkCanvas 在 canvas 内的选定图像上绘制线条,我想将 canvas 作品保存到新的图像文件中。
我在尝试保存文件后得到一张空白图像。我尝试了两种保存文件的方法。
完成的工作:
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"/>
代码隐藏
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);
}
保存文件的方法 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);
}
}
结果
想要的结果
注:图中箭头是我画的
我用这种方法得到的结果
test2.bmp(图片由于某种原因被放大了)
保存文件的方法二
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();
}
}
这种方法的结果
出于某种原因,我得到全黑图像
test2.bmp
我们将不胜感激任何帮助。对方法 2 的任何帮助都会更好。
how to save work done on canvas using InkCanvas to an image file in UWP C#?
您无需使用 RenderTargetBitmap
将 InkCanvas 保存到图像。 UWP InkCanvas
has SaveAsync method that could save the StrokeContainer
直接流到图像文件。例如。
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
{
}
}
更多细节请参考UWP simple ink代码示例场景3
更新
I am getting a blank image after trying to save the file.
以上代码只能保存InkCanvas
笔划,我查了你的代码,发现你没有在selectionCanvas
中放置任何元素。所以 selectionCanvas
的 RenderTargetBitmap
将变黑为空。请尝试用img_grid
代替
Hey sorry but now the InkToolbar is also getting copied on the image along with the ink changes i make :(
这是设计使然,RenderTargetBitmap
将呈现所有被查看的元素,对于您的场景,我们建议您将矩形覆盖 InkToolbar
或将 img_inktoolbar 可见性设置为折叠,在捕获屏幕并在完成后重置它之前。
我对方法 2 进行了一些更改并获得了预期的结果
保存文件的方法 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();
}
}
我想在我的 UWP 应用程序中保存在 canvas 上完成的工作。我正在使用 InkCanvas 在 canvas 内的选定图像上绘制线条,我想将 canvas 作品保存到新的图像文件中。
我在尝试保存文件后得到一张空白图像。我尝试了两种保存文件的方法。
完成的工作:
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"/>
代码隐藏
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);
}
保存文件的方法 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);
}
}
结果
想要的结果
注:图中箭头是我画的
我用这种方法得到的结果
test2.bmp(图片由于某种原因被放大了)
保存文件的方法二
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();
}
}
这种方法的结果
出于某种原因,我得到全黑图像
test2.bmp
我们将不胜感激任何帮助。对方法 2 的任何帮助都会更好。
how to save work done on canvas using InkCanvas to an image file in UWP C#?
您无需使用 RenderTargetBitmap
将 InkCanvas 保存到图像。 UWP InkCanvas
has SaveAsync method that could save the StrokeContainer
直接流到图像文件。例如。
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
{
}
}
更多细节请参考UWP simple ink代码示例场景3
更新
I am getting a blank image after trying to save the file.
以上代码只能保存InkCanvas
笔划,我查了你的代码,发现你没有在selectionCanvas
中放置任何元素。所以 selectionCanvas
的 RenderTargetBitmap
将变黑为空。请尝试用img_grid
代替
Hey sorry but now the InkToolbar is also getting copied on the image along with the ink changes i make :(
这是设计使然,RenderTargetBitmap
将呈现所有被查看的元素,对于您的场景,我们建议您将矩形覆盖 InkToolbar
或将 img_inktoolbar 可见性设置为折叠,在捕获屏幕并在完成后重置它之前。
我对方法 2 进行了一些更改并获得了预期的结果
保存文件的方法 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();
}
}