将画布转换为图像时,UWP 应用程序挂起



我试图将 UWP 中的画布转换为图像 (RenderTargetBitmap(。我有两个选项可以将图像返回给最终用户。

  1. StorageFile
  2. System.IO.Stream

当我使用存储文件时,一切都按预期工作。但是当我使用内存流时,应用程序会挂起。我创建了一个简单的示例来重现该问题。

<Grid Background="White" Name ="Main_Grid">
<Button Content="UIToImage" Margin="141,159,0,0" VerticalAlignment="Top" Click="UIToImageAsync"></Button>
</Grid>

private async void UIToImageAsync(object sender, RoutedEventArgs e)
{
//Pick a folder                     
var folder = KnownFolders.PicturesLibrary;
var storageFile = await folder.CreateFileAsync("Output.png", CreationCollisionOption.ReplaceExisting);
//using (var inputImgStream = await storageFile.OpenStreamForWriteAsync())//this works
using (var inputImgStream = new MemoryStream())//this doesn't work
{
//Draw a line
Windows.UI.Xaml.Shapes.Path path = new Windows.UI.Xaml.Shapes.Path();
DrawShape(path);
//The canvas to hold the above shape - line
var canvas = new Canvas();
//Add canvas to the grid in XAML
Main_Grid.Children.Add(canvas);
canvas.Children.Add(path);

//Draw the canvas to the image
RenderTargetBitmap bitmap = null;
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => 
{
bitmap = new RenderTargetBitmap();
canvas.Height = 800;
canvas.Width = 1380;
canvas.RenderTransform = new TranslateTransform { X = 1, Y = 100
};
});

//Render a bitmap image
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,async () => 
{
await bitmap.RenderAsync(canvas, 1380, 800);
});
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, inputImgStream.AsRandomAccessStream());// I suspect passing the MemoryStream is the issue. While 'StorageFile' is used there are no issues.
IBuffer pixelBuffer = await bitmap.GetPixelsAsync();
encoder.SetPixelData(
BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Ignore,
(uint)bitmap.PixelWidth,
(uint)bitmap.PixelHeight,
DisplayInformation.GetForCurrentView().LogicalDpi,
DisplayInformation.GetForCurrentView().LogicalDpi,
pixelBuffer.ToArray());
await encoder.FlushAsync(); // The application hangs here
}
}    
private void DrawShape(Windows.UI.Xaml.Shapes.Path path)
{
PathGeometry lineGeometry = new PathGeometry();
PathFigure lineFigure = new PathFigure();
LineSegment lineSegment = new LineSegment();
lineFigure.StartPoint = new Point(100, 100);
lineSegment.Point = new Point(200, 200);
lineFigure.Segments.Add(lineSegment);
path.Data = lineGeometry;
SolidColorBrush strokeBrush = new SolidColorBrush(Windows.UI.Color.FromArgb(255, 255, 0, 0));
path.Stroke = strokeBrush;
path.StrokeThickness = 5;
lineGeometry.Figures.Add(lineFigure);
}

谁能指出我是什么原因造成的?

似乎真的使用带有AsRandomAccessStream的简单MemoryStream不起作用,尽管我不确定原因。相反,您可以使用InMemoryRandomAccessStream,它将按预期工作。

但是还有另一个问题,这可能是问题的根源,或者至少它导致它在我的机器上崩溃:

//Render a bitmap image
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
CoreDispatcherPriority.Normal, async () => 
{
await bitmap.RenderAsync(canvas, 1380, 800);
});

尽管await似乎会等待RenderAsync调用完成,但不幸的是,它没有。第二个参数只是一个DispatchedHandler。此委托具有以下签名:

public delegate void DispatchedHandler()

如您所见,没有Task返回值。这意味着它将只创建一个async voidlambda。lambda将开始运行,当它到达RenderAsync时,它将开始执行它,但RunAsyncawait可能会(并且很可能(RunAsync之前完成。因此,您很可能会在bitmap仍然完全为空时开始执行bitmap.GetPixelAsync

要解决此问题,您应该将代码移动到 lambda 中:

//Render a bitmap image
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
await bitmap.RenderAsync(canvas, 1380, 800);
using (var inputImgStream = new InMemoryRandomAccessStream()) //this doesn't work
{                        
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId,
inputImgStream
); // I suspect passing the MemoryStream is the issue. While 'StorageFile' is used there are no issues.
IBuffer pixelBuffer = await bitmap.GetPixelsAsync();
Debug.WriteLine($"Capacity = {pixelBuffer.Capacity}, Length={pixelBuffer.Length}");
var pixelArray = pixelBuffer.ToArray();
encoder.SetPixelData(
BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Ignore,
(uint) bitmap.PixelWidth,
(uint) bitmap.PixelHeight,
DisplayInformation.GetForCurrentView().LogicalDpi,
DisplayInformation.GetForCurrentView().LogicalDpi,
pixelArray
);
await encoder.FlushAsync(); // The application hangs here
}
});

如您所见,您还必须将流的using块移动到 lambda 内部,因为如果它在外面,也会发生同样的命运 -using可能会在RenderAsync完成之前Dispose流。

最新更新