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

时间:2018-05-28 07:28:45

标签: c# uwp

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

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

    <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);
        }
    

    有人能指出我造成这种情况的原因吗?

1 个答案:

答案 0 :(得分:1)

使用简单的MemoryStreamAsRandomAccessStream似乎并不起作用,尽管我不确定原因。相反,您可以使用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可以(并且很可能)在<{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可能usingDispose完成之前的流。