【问题标题】:UWP application hangs when converting a canvas to image将画布转换为图像时 UWP 应用程序挂起
【发布时间】:2018-05-28 16:11:28
【问题描述】:

我试图将 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);
    }

谁能指出造成这种情况的原因?

【问题讨论】:

    标签: c# uwp


    【解决方案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 时,它将开始执行它,但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 可能 Dispose 在 @ 之前987654341@完成。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-12-08
      • 2015-10-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-26
      相关资源
      最近更新 更多