我有一个Windows 10 UWP应用程序,该应用程序将在Windows 10移动设备上运行。我的要求是捕获用户的签名。到目前为止,我只是在XAML中使用Inkcanvas,并将其连接到我的代码后面。
i然后有一个按钮,当单击时,将在Inkcanvas上进行签名,并通过WCF调用将其发送到服务器。服务器和WCF服务已经存在。它以base64序列化字符串为单位。
我知道如何获得base64,一旦我拥有图像或字节数组。但是,在我的数小时的阅读中,我发现文章/示例是为WPF或Windows 8.1编写的,并且在Windows 10 UWP上不起作用。另外,在我发现将有效的示例中,我唯一的选择是将签名保存为gif。
我看到我可以像这个
一样调用getTrokes() var strokeCollection = cvsSignature.InkPresenter.StrokeContainer.GetStrokes();
这将使我仅读取inkstroke的读取列表。我想我可以迭代该列表并构建字节数组吗?我该怎么做?看来这不是有效的?
否则,我认为我可以将流从文件流更改为内存流,但我想这是不可能的,或者我缺少一些东西。我正在尝试这个
using (var inkMemStream = new MemoryStream())
{
await cvsSignature.InkPresenter.StrokeContainer.SaveAsync(inkMemStream);
}
但是,使用这种方法,我会得到一个例外,我无法从System.io.memorystream转换为Windows.storage.storage.Streams.ioutputStream
谢谢!
我找到了一种方法,如果您不想将Inkcanvas保存到文件或GIF,则在内存中操纵它。我实际上有三个选择。请注意,对于其中的某些情况,您需要添加对Win2D.UWP的引用,可以通过搜索确切的名称在Nuget上找到。它由Microsoft提供。
-
将Inkcanvas转换为字节数组:
private byte[] ConvertInkCanvasToByteArray() { //First, we need to get all of the strokes in the canvas var canvasStrokes = myCanvas.InkPresenter.StrokeContainer.GetStrokes(); //Just as a check, make sure to only do work if there are actually strokes (ie not empty) if (canvasStrokes.Count > 0) { var width = (int)myCanvas.ActualWidth; var height = (int)myCanvas.ActualHeight; var device = CanvasDevice.GetSharedDevice(); //Create a new renderTarget with the same width and height as myCanvas at 96dpi var renderTarget = new CanvasRenderTarget(device, width, height, 96); using (var ds = renderTarget.CreateDrawingSession()) { //This will clear the renderTarget with a clean slate of a white background. ds.Clear(Windows.UI.Colors.White); //Here is where we actually take the strokes from the canvas and draw them on the render target. ds.DrawInk(myCanvas.InkPresenter.StrokeContainer.GetStrokes()); } //Finally, this will return the render target as a byte array. return renderTarget.GetPixelBytes(); } else { return null; } }
-
如果您需要的只是像素的字节数组,则完成。但是,如果您现在想生成图像,则使用WritableBitMap进行此操作。这是可以称为执行此操作的同步和异步方法。
private WriteableBitmap GetSignatureBitmapFull() { var bytes = ConvertInkCanvasToByteArray(); if (bytes != null) { var width = (int)cvsSignature.ActualWidth; var height = (int)cvsSignature.ActualHeight; var bmp = new WriteableBitmap(width, height); using (var stream = bmp.PixelBuffer.AsStream()) { stream.Write(bytes, 0, bytes.Length); return bmp; } } else return null; } private async Task<WriteableBitmap> GetSignatureBitmapFullAsync() { var bytes = ConvertInkCanvasToByteArray(); if (bytes != null) { var width = (int)cvsSignature.ActualWidth; var height = (int)cvsSignature.ActualHeight; var bmp = new WriteableBitmap(width, height); using (var stream = bmp.PixelBuffer.AsStream()) { await stream.WriteAsync(bytes, 0, bytes.Length); return bmp; } } else return null; }
-
最后,这是一种异步方法,如果您想这样做,我用来在位图上进行农作物。这种方法的关键是注意如何使用这种方法,您不必先将Pixel字节作为数组。您只需从画布中获得笔触,然后将其直接保存到内存流中
private async Task<WriteableBitmap> GetSignatureBitmapCropped() { try { var canvasStrokes = cvsSignature.InkPresenter.StrokeContainer.GetStrokes(); if (canvasStrokes.Count > 0) { var bounds = cvsSignature.InkPresenter.StrokeContainer.BoundingRect; var xOffset = (uint)Math.Round(bounds.X); var yOffset = (uint)Math.Round(bounds.Y); var pixelWidth = (int)Math.Round(bounds.Width); var pixelHeight = (int)Math.Round(bounds.Height); using (var memStream = new InMemoryRandomAccessStream()) { await cvsSignature.InkPresenter.StrokeContainer.SaveAsync(memStream); var decoder = await BitmapDecoder.CreateAsync(memStream); var transform = new BitmapTransform(); var newBounds = new BitmapBounds(); newBounds.X = 0; newBounds.Y = 0; newBounds.Width = (uint)pixelWidth; newBounds.Height = (uint)pixelHeight; transform.Bounds = newBounds; var pdp = await decoder.GetPixelDataAsync( BitmapPixelFormat.Bgra8, BitmapAlphaMode.Straight, transform, ExifOrientationMode.IgnoreExifOrientation, ColorManagementMode.DoNotColorManage); var pixels = pdp.DetachPixelData(); var cropBmp = new WriteableBitmap(pixelWidth, pixelHeight); using (var stream = cropBmp.PixelBuffer.AsStream()) { await stream.WriteAsync(pixels, 0, pixels.Length); } return cropBmp; } } else { return null; } } catch (Exception ex) { return null; } }
希望这有助于在Windows 10 Universal中使用InkCanvas时提供一些替代方案。
似乎我唯一的选择是将签名保存为gif。
GIF
不是唯一的选择。官方样本只显示GIF
,但您也可以将Inkstokes Collection保存到JPG
和PNG
。代码如下:
var savePicker = new Windows.Storage.Pickers.FileSavePicker();
savePicker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.PicturesLibrary;
savePicker.FileTypeChoices.Add("Gif,JPG,PNG", new System.Collections.Generic.List<string> { ".jpg" ,".gif",".png"});
Windows.Storage.StorageFile file = await savePicker.PickSaveFileAsync();
但是,使用这种方法,我会得到一个例外,我无法从System.io.memorystream转换为Windows.storage.storage.Streams.ioutputStream
如您所见,它不能直接从System.IO.MemoryStream
转换为Windows.Storage.Streams.IOutputStream
。您需要读取刚刚保存为ImageSource的图像文件。如您所知,一旦拥有图像或字节数组,如何获得base64。因此,我们只需要从图像文件中获取图像或字节数组。
将图像文件读为字节数组,然后将内存流读取如下
if (inkCanvas.InkPresenter.StrokeContainer.GetStrokes().Count > 0)
{
var savePicker = new Windows.Storage.Pickers.FileSavePicker();
savePicker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.PicturesLibrary;
savePicker.FileTypeChoices.Add("Gif,JPG,PNG", new System.Collections.Generic.List<string> { ".jpg", ".gif", ".png" });
Windows.Storage.StorageFile file = await savePicker.PickSaveFileAsync();
if (null != file)
{
using (IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
await inkCanvas.InkPresenter.StrokeContainer.SaveAsync(stream);
}
using (IRandomAccessStream streamforread = await file.OpenAsync(FileAccessMode.Read))
{
WriteableBitmap inkimagesource = new WriteableBitmap(50, 50);
inkimagesource.SetSource(streamforread);
byte[] imageBuffer = inkimagesource.PixelBuffer.ToArray();
MemoryStream ms = new MemoryStream(imageBuffer);
}
}
}
将文件读取为图像源,只需更改文件读取代码,如下所示:
using (IRandomAccessStream streamforread = await file.OpenAsync(FileAccessMode.Read)
{
var bitmap = new BitmapImage();
bitmap.SetSource(streamforread);
}
我想我可以迭代该列表并构建字节数组吗?我该怎么做?看来这不是有效的?
目前似乎没有API可以直接将InkStroke
集合构建为字节数组。saveasync()方法已经帮助您将InkStroke
集合保存到流中,您可以使用上面的代码来使用它。