使用 WCF 调用发送 InkCanvas 签名
Send InkCanvas signature using WCF call
我有一个 Windows 10 UWP 应用程序,可以 运行 在 Windows 10 Mobile 上使用。我的一个要求是捕获用户的签名。到目前为止,我只是在 XAML 中使用 InkCanvas 并将其连接到我后面的代码。
然后我有一个按钮,单击该按钮时,将在 InkCanvas 上获取签名并通过 WCF 调用将其发送到服务器。服务器和 WCF 服务已经存在。它将签名图像作为 base64 序列化字符串。
一旦我有了图像或字节数组,我就知道如何获取 base64。然而,在我长时间的阅读中,我发现 articles/examples 是为 WPF 或 Windows 8.1 编写的,并且不适用于 Windows 10 UWP。此外,在我发现可行的示例中,似乎我唯一的选择是将签名保存为 GIF 文件。
我发现我可以像这样调用 GetStrokes()
var strokeCollection = cvsSignature.InkPresenter.StrokeContainer.GetStrokes();
这将 return 我 InkStroke 的只读列表。我想我可以迭代该列表并构建一个字节数组?我该怎么做?好像这样效率不高?
否则,我想我可以将流从文件流更改为内存流,但我想这不可能,或者我遗漏了一些东西。我正在尝试这个
using (var inkMemStream = new MemoryStream())
{
await cvsSignature.InkPresenter.StrokeContainer.SaveAsync(inkMemStream);
}
但是使用这种方法我得到一个异常,我无法从 System.IO.MemoryStream 转换为 Windows.Storage.Streams.IOutputStream
谢谢!
it seems my only option is to save the signature to file as a GIF.
GIF
不是唯一的选择。 official sample 只显示 GIF
,但您也可以将 InkStokes 集合保存到 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();
But with this type of approach I get an exception that I cannot convert from System.IO.MemoryStream to Windows.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);
}
I guess I can iterate that list and build a byte array? How would I do that? It seems this is not efficient?
目前好像没有API可以直接将InkStroke
集合构建为字节数组。 SaveAsync() 方法已经帮你将 InkStroke
集合保存到流中,你可以使用上面的代码来使用它。
如果您不想将 InkCanvas 保存到文件或作为 GIF,我找到了一种在内存中操作它的方法。我实际上有以下三个选项。请注意,对于其中的某些内容,您需要添加对 Win2D.uwp 的引用,可以通过搜索确切名称在 NuGet 上找到它。由微软提供。
将 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;
}
}
如果您只需要一个像素字节数组,您就完成了。但是,如果您现在想要生成图像,则可以使用 WriteableBitmap 来实现。以下是可以调用的同步和异步方法。
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;
}
最后,这里是一个异步方法,如果你想这样做,我可以使用它来对位图进行裁剪。这种方法的关键是注意如何使用这种方法,您不必首先将像素字节作为数组获取。您只需从 canvas 中获取笔画,然后将其直接保存到内存流中
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 时提供一些替代方案。
我有一个 Windows 10 UWP 应用程序,可以 运行 在 Windows 10 Mobile 上使用。我的一个要求是捕获用户的签名。到目前为止,我只是在 XAML 中使用 InkCanvas 并将其连接到我后面的代码。
然后我有一个按钮,单击该按钮时,将在 InkCanvas 上获取签名并通过 WCF 调用将其发送到服务器。服务器和 WCF 服务已经存在。它将签名图像作为 base64 序列化字符串。
一旦我有了图像或字节数组,我就知道如何获取 base64。然而,在我长时间的阅读中,我发现 articles/examples 是为 WPF 或 Windows 8.1 编写的,并且不适用于 Windows 10 UWP。此外,在我发现可行的示例中,似乎我唯一的选择是将签名保存为 GIF 文件。
我发现我可以像这样调用 GetStrokes()
var strokeCollection = cvsSignature.InkPresenter.StrokeContainer.GetStrokes();
这将 return 我 InkStroke 的只读列表。我想我可以迭代该列表并构建一个字节数组?我该怎么做?好像这样效率不高?
否则,我想我可以将流从文件流更改为内存流,但我想这不可能,或者我遗漏了一些东西。我正在尝试这个
using (var inkMemStream = new MemoryStream())
{
await cvsSignature.InkPresenter.StrokeContainer.SaveAsync(inkMemStream);
}
但是使用这种方法我得到一个异常,我无法从 System.IO.MemoryStream 转换为 Windows.Storage.Streams.IOutputStream
谢谢!
it seems my only option is to save the signature to file as a GIF.
GIF
不是唯一的选择。 official sample 只显示 GIF
,但您也可以将 InkStokes 集合保存到 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();
But with this type of approach I get an exception that I cannot convert from System.IO.MemoryStream to Windows.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);
}
I guess I can iterate that list and build a byte array? How would I do that? It seems this is not efficient?
目前好像没有API可以直接将InkStroke
集合构建为字节数组。 SaveAsync() 方法已经帮你将 InkStroke
集合保存到流中,你可以使用上面的代码来使用它。
如果您不想将 InkCanvas 保存到文件或作为 GIF,我找到了一种在内存中操作它的方法。我实际上有以下三个选项。请注意,对于其中的某些内容,您需要添加对 Win2D.uwp 的引用,可以通过搜索确切名称在 NuGet 上找到它。由微软提供。
将 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; } }
如果您只需要一个像素字节数组,您就完成了。但是,如果您现在想要生成图像,则可以使用 WriteableBitmap 来实现。以下是可以调用的同步和异步方法。
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; }
最后,这里是一个异步方法,如果你想这样做,我可以使用它来对位图进行裁剪。这种方法的关键是注意如何使用这种方法,您不必首先将像素字节作为数组获取。您只需从 canvas 中获取笔画,然后将其直接保存到内存流中
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 时提供一些替代方案。