2

我正在使用 WritableBitmap 在 WPF 中绘制快速的 2d 图形。

问题是,我的 WritableBitmap 与 Image 控件 1:1 的像素大小不匹配。当我尝试根据 Image 控件的大小更改事件制作新的 WritableBitmap 时,性能很糟糕。

如何使用 WritableBitmap 以 1:1 呈现 WPF 控件的像素?

您可以在 github 上看到我的代码的 WPF 和 Windows.Forms 版本。

该应用程序是一个名为 SoundLevelMonitor 的小型迷你应用程序,它监控应用程序的音频电平并绘制带有图例的连续移动图形。下面是 Windows.Forms 版本的屏幕截图。WPF 版本基本相同,只是图像是固定大小并拉伸以适合窗口。

在此处输入图像描述

在开始使用 WritableBitmap 之前,我尝试使用 UIElement,但绘图性能很糟糕。一个问题是触发重绘需要InvalidateVisual()导致缓慢的重新布局。另一个问题是使用 DrawingContext 绘制文本非常慢。

所以我发现这个建议使用 GDI 写入 WriteableBitmap,它实际上工作得很好,而且速度非常快。(WPF 版本感觉比 windows 窗体版本响应更快!)

但是,WriteableBitmap 是一个固定大小的后备存储,WPF 正在按比例放大以适应 Image 控件。我希望 WriteableBitmap 成为控件的 1:1 像素后备存储,即使在调整控件大小时也是如此。

我尝试使用两者OnRenderLayoutUpdated触发创建一个新的大小匹配的 WriteableBitmap,但它们都非常慢。可能是因为设置 Image.Source 正在触发重新布局。

有任何想法吗?

4

1 回答 1

2

我最大的发现是我不需要使用位图来获得我想要的结果。我可以创建一个DrawingGroup作为后备存储,在 OnRender 期间将其绘制到绘图上下文中,然后仍然可以在以后随时更新它。

DrawingGroup backingStore = new DrawingGroup();

protected override void OnRender(DrawingContext drawingContext) {      
    base.OnRender(drawingContext);            

    Render(); // put content into our backingStore
    drawingContext.DrawDrawing(backingStore);
}

// I can call this anytime, and it'll update my visual drawing
// without ever triggering layout or OnRender()
private void Render() {            
    var drawingContext = backingStore.Open();
    Render(drawingContext);
    drawingContext.Close();            
}

我还了解到StreamGeometrywithFreeze()明显比其他绘图方法快,因为它针对无动画进行了优化。


与我的位图尝试相关——当图像控件大小更改时通知的正确方法是监听SizeChanged,它仅在控件更改大小时触发。在这个事件处理程序中,我必须创建一个新的 WriteableBitmap 大小Image.RenderSize并将其分配给 Image.Source。

此外,在图像控件实例化中,我必须将其设置为Stretch="Fill"使其完全填充它的包含框,而不是保持图像的纵横比。

LayoutUpdated每次控制内容更新时都会触发,这就是导致我最初的性能问题的原因。

我还发现我可以使用RenderTargetBitmap而不是WriteableBitmap,然后我可以向它发出 WPF 绘图命令,而不是使用 GDI 图形上下文。

..我可以捕捉到该CompositionTarget.Rendering事件以使其每帧都重新绘制。

于 2017-06-07T21:39:06.213 回答