【问题标题】:howto efficiently have a WritableBitmap which matches a WPF control pixels 1:1?如何有效地拥有与 WPF 控件像素 1:1 匹配的 WriteableBitmap?
【发布时间】:2017-06-07 19:02:51
【问题描述】:

我正在使用 WritableBitmap 在 WPF 中快速绘制二维图形。

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

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

您可以看到my code on github 的 WPF 和 Windows.Forms 版本。

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

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

所以我找到了这个suggestion to write to WriteableBitmap using GDI,它实际上运行良好,而且速度非常快。 (WPF 版本感觉比 windows 窗体版本响应更快!)

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

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

有什么想法吗?

【问题讨论】:

    标签: c# wpf writablebitmap


    【解决方案1】:

    我最大的发现是我不需要使用位图来获得我想要的结果。我可以创建一个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();            
    }
    

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


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

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

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

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

    ..我可以捕捉到CompositionTarget.Rendering 事件,让它在每一帧都被重绘。

    【讨论】:

      猜你喜欢
      • 2022-11-27
      • 2016-10-05
      • 1970-01-01
      • 2018-09-08
      • 1970-01-01
      • 2015-12-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多