【问题标题】:How to draw graphics as efficiently as possible in WPF如何在 WPF 中尽可能高效地绘制图形
【发布时间】:2011-11-14 01:25:35
【问题描述】:

我正在创建一个严重依赖图节点树的工具。当前的实现是用 Java 完成的,我将它移植到 C# 上的通用代码库,因此它可以被各种渲染实现使用,也因为我想使用 WPF 的强大功能来实现用户友好的界面。

浏览了一天,发现了各种通过 WPF 绘制矢量图形的方法。

This guy 谈到 WPF 开发人员可以选择的不同层。由于我想首先使用 WPF PURELY 进行渲染,所以我想在“视觉层”上工作。

然后我遇到了类似的事情: DrawingVisual, GeometryDrawing, FrameworkElement / UIElement / 形状

所以,所有不同的实现最终以完全不同的方式实现相同的效果,这让我有点不知所措。

Graph-Node 库已经移植到了 C# 中,并带有它的所有逻辑(包括碰撞检测和鼠标拖动)。由于它是在考虑图形渲染器(如 XNA、SlimDX、OpenTK 等)的情况下制作的,因此在性能方面实现 WPF 渲染器的最佳方式是什么(例如,它将绘制图形库告诉它的任何内容画画?

基本上,生成的 WPF 控件充当画布,但它必须是超级轻量级​​的,除了为我提供一种绘制圆圈、线条和其他形状的方法之外,它必须没有任何整洁的 WPF 功能:)

编辑:

我基本上想知道:要走的路是什么?我是否将 Canvas 扩展为我的图形的“主机”,然后添加我的 UIElement 的自定义实现?或者我可以有一个可以绘制所有东西的课程(例如,一个超级超级图形)。很像在 GDI 中重写 OnPaint 或在 Java 中重写 Paint-method(它提供了一个 Graphics 对象来做所有事情)。

【问题讨论】:

  • 您希望在正常图中绘制多少个节点?
  • 至少有数百个。图形在迭代中动态生成。每次迭代可以使节点数翻倍。我开始认为我最好使用某种 DirectX 实现(也许就剔除而言?)。
  • @Lennard:请记住,WPF 在后台使用 DirectX - 您可以免费获得大部分内容。 Canvas+Drawing 可以轻松扩展到数千个节点(至少在 Vista+ 中),尽管它使用的是 DirectX,所以它有点依赖显卡。

标签: c# wpf graphics graph vector-graphics


【解决方案1】:

我建议阅读Optimizing Performance: 2D Graphics and Imaging

基本上,Drawing 对象的重量通常比Shapes 轻。这可能是您想要使用的。

【讨论】:

  • 我已经阅读了那个特定的页面,它只会让我对做什么和选择什么感到更加困惑。那里描述的所有方法似乎仍然与框架层挂钩,而微软博客的人特别提到了一个没有花哨的东西的可视层。
  • 我基本上想知道:要走的路是什么?我是否将 Canvas 扩展为我的图形的“主机”,然后添加我的 UIElement 的自定义实现?或者我可以有一个可以绘制所有东西的课程(例如,一个超级超级图形)。很像覆盖 GDI 中的 OnPaint 或 Java 中的 Paint-method(它提供了一个 Graphics 对象来做所有事情)。
  • @Lennard:“视觉层”是框架层的一部分。 WPF 中的所有内容都在可视树和逻辑树之间使用并拆分。在您的特定情况下,听起来您想使用 GeometryDrawing 和相关类。这将为您提供一种干净、(相对)轻量级的方式来处理数据的绘制,而无需使用 Shape* 类添加的大量其他内容。
  • @Lennard:您不必像 WinForms 那样考虑这一点——您只需使用 Canvas,并将绘图对象添加到其中。 WPF 将为您处理实际的渲染,并且非常高效,尤其是在您使用 DrawingGroup 时,因为它可以很好地缓存内容。
  • @Lennard:您可以使用 Window.OnRender (msdn.microsoft.com/en-us/library/…) - 但是,对于这样的事情,使用绘图通常要好得多。如果您使用 DrawingGroup,WPF 的缓存通常会执行更好,然后像在 Windows 窗体/Java 中那样执行您自己的绘图。它通过 Direct3D 缓存和渲染所有内容,因此速度非常快。
【解决方案2】:

通常,使用较低级别的服务可以获得更好的性能。在WPF 中,这意味着Drawing 对象族。你得到的只是:DrawingDrawingGroupGeometryDrawingGlyphRunDrawingImageDrawingVideoDrawing。但是,它们足以满足所有需求。使用这些类型对 WPF 非常友好,因为 Drawing 是 WPF 与您的 GPU 加速器交换的概念单元,如果可能的话,可能会在那里保留和管理它。这是可行的,因为Drawing 是用可移植矢量绘图基元表示的。

然而,一旦您开始围绕 Drawings 重新构建您的应用程序,您可能需要与仍然基于 UIElementFrameworkElement 等的更高级别代码进行一些互操作。我还没有做的一件事发现 WPF 内置是一种简单的方法,可以以最低开销的方式将绘图包装为 FrameworkElementDrawingVisual 不是一个完整的解决方案,因为它仅派生自 Visual——这意味着它仍然需要一个托管元素。

以下类将直接托管任何 WPF Drawing,而不使用中间 DrawingVisual。我添加了对FrameworkElementMargin 属性的支持(如果未使用则不会降低性能),但仅此而已。由于 WPF 的单个渲染线程,缓存单个 TranslateTransform 对象以实现边距是安全且容易的。我建议您只提供已冻结的图纸;事实上,在我使用的版本中,我在构造函数中有一个断言。

public class DrawingElement : FrameworkElement
{
    static readonly TranslateTransform tt_cache = new TranslateTransform();

    public DrawingElement(Drawing drawing)
    {
        this.drawing = drawing;
    }
    readonly Drawing drawing;

    TranslateTransform get_transform()
    {
        if (Margin.Left == 0 && Margin.Top == 0)
            return null;
        tt_cache.X = Margin.Left;
        tt_cache.Y = Margin.Top;
        return tt_cache;
    }
    protected override Size MeasureOverride(Size _)
    {
        var sz = drawing.Bounds.Size;
        return new Size
        {
            Width = sz.Width + Margin.Left + Margin.Right,
            Height = sz.Height + Margin.Top + Margin.Bottom,
        };
    }
    protected override void OnRender(DrawingContext dc)
    {
        var tt = get_transform();
        if (tt != null)
            dc.PushTransform(tt);
        dc.DrawDrawing(drawing);
        if (tt != null)
            dc.Pop();
    }
};

[edit:] 这对于将 WPF Drawing 插入到 InlineUIContainer.Child 属性中也很有用(即使用 TextBlock.InlinesCollection 来更丰富地格式化 TextBlock 的内容)。

【讨论】:

    【解决方案3】:

    DrawingVisual 似乎是一个有效的选择:

    DrawingVisual 是一个轻量级的绘图类,用于 渲染形状、图像或文本。这个类被认为是轻量级的 因为它不提供布局或事件处理,这改进了 它的表现。出于这个原因,图纸是背景的理想选择 和剪贴画。

    来源:Using DrawingVisual Objects

    所以这似乎完全符合您的要求,Canvas SUPER 轻量级。

    【讨论】:

    • 请在 Reed 的回答中查看我的 2 个注释。我将更新我的第一篇文章以使问题更加具体。
    • DrawingVisual dows 似乎并没有你害怕的那么高。如果您想要更轻量级或原生,即使在 WPF 中,您仍然可以使用 OpenGL。或者也读一下:stackoverflow.com/questions/1912469/directx-and-wpf
    • 我知道 OpenGL/DirectX 通过 D3DImage 在 WPF 中工作。只是我想使用 WPF 来渲染这个图形,因为它已经带有绘制形状的方法,而使用 XNA/Other 我必须创建自己的库来做到这一点和/或找到一个现有的库(我知道一些,但由于 WPF 本身就支持这一点,而且它本质上是 DirectX...)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-09
    • 1970-01-01
    • 2017-03-21
    • 2020-12-19
    • 2015-07-08
    • 2013-08-05
    相关资源
    最近更新 更多