如何在 WPF 中尽可能高效地绘制图形

发布于 2024-12-04 00:06:40 字数 1296 浏览 1 评论 0原文

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

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

这个人谈论 WPF 开发人员可以选择的不同层。因为我想首先纯粹使用 WPF 进行渲染,所以我想在“视觉层”上工作。

然后我遇到了这样的事情: DrawingVisual, 几何绘图FrameworkElement / UIElement / Shapes

因此,我对最终完成的所有不同实现感到有点不知所措以完全不同的方式相同。

Graph-Node 库已被移植到 C# 中,并包含其所有逻辑(包括碰撞检测和鼠标拖动)。由于它是根据图形渲染器(如 XNA、SlimDX、OpenTK 等)制作的,因此在性能方面实现 WPF 渲染器的最佳方式是什么(例如,它将绘制图形库告诉它的任何内容) 基本上,生成的 WPF 控件充当画布

,但它必须是超级轻量级​​的,除了为我提供绘制圆形、线条和其他形状的方法之外,没有任何简洁的 WPF 功能:)

编辑:

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

I am creating a tool which relies heavily on graph-node trees. The current implementation is done in Java and I'm porting it to a generic code-base on C#, so it can be used by various rendering implementations and also because I want to use the power of WPF for a user-friendly interface.

After browsing around for a day, I came across various methods to draw Vector-graphics through WPF.

This guy speaks about different layers within WPF developers can choose from. As I want to use WPF PURELY for his rendering at first, I want to work on the "Visual Layer".

I then came across things like:
DrawingVisual,
GeometryDrawing,
FrameworkElement / UIElement / Shapes

So, I'm a bit overwhelmed by all the different implementations that do eventually the same in totally different ways.

The Graph-Node library has been ported to C# already with all it's logic (including collision detection and dragging with mouse). As it is made with graphic-renderers in mind (like XNA, SlimDX, OpenTK, etc.), what would be the best way in terms of performance to implement a WPF renderer (as in, it will draw whatever the graph library tells it to draw?

Basically, the resulting WPF control acts as a canvas, but it has to be SUPER lightweight and not have any neat WPF features besides providing me a way to draw my circles, lines and other shapes :)

EDIT:

I basically want to know: What is the way to go? Do I extend Canvas as "Host" for my graphics and then add my custom implementation of a UIElement? Or can I have one class which can draw EVERYTHING (as in, one mega super ultra graphic). Much like overriding OnPaint in GDI or Paint-method in Java (which gives a Graphics object to do everything with).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

幸福%小乖 2024-12-11 00:06:40

我建议阅读 优化性能:2D 图形和成像

一般来说,Drawing 对象的重量比 Shapes 轻。这可能就是您想要使用的。

I'd recommend reading Optimizing Performance: 2D Graphics and Imaging.

Basically, Drawing objects will be lighter weight than Shapes, in general. This is probably what you want to use.

酒绊 2024-12-11 00:06:40

一般来说,较低级别的服务可以获得更好的性能。在 WPF 中,这意味着 Drawing 对象系列。您获得的所有内容是:DrawingDrawingGroupGeometryDrawingGlyphRunDrawingImageDrawing、和VideoDrawing。然而,它们足以满足所有需求。使用这些类型对于 WPF 非常友好,因为 Drawing 是 WPF 与 GPU 加速器交换的概念单元,如果可能的话,可能会在那里保留和管理它。这是可行的,因为 Drawing 是用便携式矢量绘图基元表示的。

然而,一旦您开始围绕 Drawings 重新构建应用程序,您可能需要与仍基于 UIElementFrameworkElement将绘图包装为 FrameworkElement 的简单方法。 DrawingVisual 不是一个完整的解决方案,因为它仅派生自 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();
    }
};

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

Generally, better performance is obtained with lower-level services. In WPF, this means the Drawing family of objects. All you get are: Drawing, DrawingGroup, GeometryDrawing, GlyphRunDrawing, ImageDrawing, and VideoDrawing. However, they are sufficient for all needs. Using these types is very friendly with WPF because Drawing is the conceptual unit that WPF exchanges with your GPU accelerator, possibly retaining and managing it there if possible. This works because the Drawing is expressed in terms of portable vector drawing primitives.

Once you start re-architecting your app around Drawings however, you might need some interop with your higher-level code which is still based on UIElement, FrameworkElement, etc. One thing that I haven't found built-in to WPF is a simple way to wrap a Drawing as a FrameworkElement in the lowest-overhead way possible. DrawingVisual isn't a complete solution, because it only derives from Visual--meaning it still requires a hosting element.

The following class will host any WPF Drawing directly without using an intermediate DrawingVisual. I added support for FrameworkElement's Margin property (with no performance penalty if unused) but little else. Because of WPF's single rendering thread it's safe and easy to cache a single TranslateTransform object to implement the margin. I'd recommend that you supply only drawings which have been Frozen; in fact, in the version that I use, I have an assert to that effect in the constructor.

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:] This is also useful for inserting a WPF Drawing into the InlineUIContainer.Child property (i.e. using TextBlock.InlinesCollection to format the contents of the TextBlock more richly).

流星番茄 2024-12-11 00:06:40

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

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

来源:使用 DrawingVisual 对象

所以这似乎绝对是您所要求的,超轻帆布。

the DrawingVisual seems to be a valid choice:

The DrawingVisual is a lightweight drawing class that is used to
render shapes, images, or text. This class is considered lightweight
because it does not provide layout or event handling, which improves
its performance. For this reason, drawings are ideal for backgrounds
and clip art.

source: Using DrawingVisual Objects

so this seems to be absolutely what you ask, a Canvas SUPER lightweight.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文