我尝试将UWP中的画布转换为图像(RenderTargetBitmap
)。我有两个选项可以将图像返回给最终用户。
StorageFile
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);
}
有人能指出我造成这种情况的原因吗?
答案 0 :(得分:1)
使用简单的MemoryStream
和AsRandomAccessStream
似乎并不起作用,尽管我不确定原因。相反,您可以使用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 void
lambda。 lambda将开始运行,当它到达RenderAsync
时,它将开始执行它,但是RunAsync
await
可以(并且很可能将} strong>)在<{1}}之前完成。因此,当RunAsync
仍然完全为空时,您可能会开始执行bitmap.GetPixelAsync
。
要解决此问题,您应该在lambda中移动代码:
bitmap
正如您所看到的,您还必须在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
可能using
在Dispose
完成之前的流。