【问题标题】:Best way to write thousands of strokes in WPF canvas在 WPF 画布中书写数千笔画的最佳方法
【发布时间】:2018-06-21 01:40:34
【问题描述】:

我必须在 Wacom 平板电脑上实现触控书写(就像在纸上一样),我需要在 WPF Canvas 中渲染每秒最多 200 笔。问题是,在大约 20 秒的持续写入之后,它会有点滞后,并且滞后会增加。我将所有笔画存储在 HashSet 中。我考虑在 HashSet 包含超过 600 个元素并将其设置为背景图像并清除 HashSet 时进行屏幕截图,但是在大约 30 个屏幕截图之后它变得有点模糊。你知道如何让它变得更好吗?

【问题讨论】:

  • 你可以使用 WriteableBitmap
  • 会试试的。感谢回复
  • Canvas 还是 InkCanvas?
  • 画布。它必须是画布,因为 inkCanvas 不会像我们希望的那样处理鼠标事件

标签: c# .net wpf canvas


【解决方案1】:

您可以使用WriteableBitmap。这非常快,但它通过将单个字节写入位图来工作,因此如果您需要对绘图应用效果,那么您必须自己编写它们。这是一个如何使用WriteableBitmap 的示例。在调试模式下,在我的计算机上绘制 1600 x 1200 像素图像上的 1000 条线(每条线由 1600 个点组成)大约需要 100 毫秒,但我确信这可以优化。

我只是随机画线,但是您可以从Canvas 获取事件并捕获手写笔的位置并在每次笔划后传递它们。

public partial class MainWindow : Window
{
    private WriteableBitmap _bitmap;
    private readonly Random _random = new Random();
    private readonly Stopwatch _stopwatch = new Stopwatch();
    private const int White = 0x00000000;
    private const int Red = 0x00FF0000;

    private int _width;
    private int _height;

    public MainWindow()
    {
        InitializeComponent();
        CanvasImage.Loaded += OnLoaded;
    }

    private async void OnLoaded(object sender, RoutedEventArgs e)
    {
        _width = (int)DrawableCanvas.ActualWidth;
        _height = (int)DrawableCanvas.ActualHeight;
        _bitmap = new WriteableBitmap(_width, _height, 96, 96, PixelFormats.Bgr32, null);
        CanvasImage.Source = _bitmap;

        while (true)
        {
            unsafe
            {
                for (var index = 0; index < _width * _height; index++)
                    *((int*)_bitmap.BackBuffer + index) = White;
            }

            _stopwatch.Start();

            for (var index = 0; index < 1000; index++)
            {
                var start = _random.Next(0, _width);
                var points = Enumerable.Range(0, _width).Select(x => new Point((x + start) % _width, x % _height));
                UpdateImage(points);
            }

            Debug.WriteLine($"Last 1000 draws took: {_stopwatch.ElapsedMilliseconds} ms");
            _stopwatch.Reset();

            await Task.Delay(300);
        }
    }

    private void UpdateImage(IEnumerable<Point> points)
    {
        _bitmap.Lock();

        foreach (var point in points)
        {
            var x = (int)point.X;
            var y = (int)point.Y;
            var offset = _width * y + x;
            unsafe
            {
                *((int*)_bitmap.BackBuffer + offset) = Red;
            }
        }

        _bitmap.AddDirtyRect(new Int32Rect(0, 0, _width, _height));
        _bitmap.Unlock();
    }
}

并查看:

<Grid>
    <Border Width="1604" Height="1204" BorderThickness="2" BorderBrush="Blue">
        <Canvas x:Name="DrawableCanvas">
            <Image x:Name="CanvasImage"></Image>
        </Canvas>
    </Border>
</Grid>

【讨论】:

  • 感谢这个解决方案,不幸的是,不安全的代码( ((int)_bitmap.BackBuffer + offset) = Red; )抛出 System.AccessViolationException: 'Attempted to read or write protected记忆。这通常表明其他内存已损坏。当我用每秒给我 200 点的笔写字时。你有什么解决办法吗?
  • @Hawex 你的指针计算有问题。您必须仔细检查如何修改偏移量。如果您在计算时遇到问题,最好在此站点上为其创建一个单独的问题。
  • 你确定是指针的问题吗?位图添加一些点,写入时随机抛出异常。
  • @Hawex 我很确定,根据你写的。您可能过多地增加或减少偏移量,它会溢出位图的BackBuffer。还有其他原因导致它可能无法正常工作。没有看到代码,我无法 100% 地告诉你原因是什么。请为此创建一个单独的问题。
  • 你是对的。我试图在位图中的像素上写字。现在一切似乎都很好。
猜你喜欢
  • 2014-02-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-03
  • 1970-01-01
  • 2012-01-10
相关资源
最近更新 更多