在NSView中绘制大量数据,还是其他什么?

4
我有数十万个时间序列数据点需要向用户呈现。我目前的解决方案是使用第三方库将数据渲染成PNG格式,然后将该PNG加载到NSImage中,并在滚动视图中显示它。这个方法非常好,但存在以下问题:
  1. 超过32k像素宽度的NSImages无法正确显示
  2. 我想能够快速轻松地缩放数据
  3. 从磁盘读取和写入数据很麻烦

我的当前尝试是直接在NSView上绘制NSBezierPath。视图渲染得非常漂亮,但非常缓慢,即使我一次只绘制有限的点集。每次滚动时都需要重新绘制,这也很慢。

作为一个相对较新的Cocoa程序员,我相信我错过了一些更好的方法来做到这一点。哪种是“正确”的方法?

6个回答

7
我的当前尝试是直接在NSView上绘制NSBezierPaths。视图渲染效果很好,但速度非常慢,即使我一次只绘制有限的点。每次滚动时都需要重新绘制,这也很慢。
在采取任何激进的解决方案之前,请尝试以下简单步骤:
  1. 剪切。 使用NSRectClip,将作为参数传递给drawRect:的矩形作为参数。这只是一个一行代码。
  2. 在填充路径之前进行简单的矩形测试。 对于每个路径,获取其边界并使用NSIntersectsRect测试其是否在矩形内。
  3. 在描边路径之前进行稍微复杂一点的矩形测试。 类似于上一步,但是您需要通过线宽来扩展矩形(作为参数接收的那个矩形),因为一半的描边会落在路径外部。对于每条路径,获取线宽并取反它(delta = -[path lineWidth] - 是的,您必须包括那个负号),然后将结果作为两个参数传递给NSInsetRect。确保保留原始矩形,因为不同的路径可能具有不同的线宽。
这个想法很简单,就是尽量少绘制。使用NSRectClip设置剪切路径可以减少绘制操作的位块传输量。排除完全落在绘制矩形框之外的路径将为这些路径节省更昂贵的剪切操作。

当然,在每个步骤中进行性能分析以确保不会使事情变慢。您可能需要设置某种帧速率计数器。

还有一件事:获取每个路径的边界可能很昂贵。(再次进行性能分析。)如果是这样,您可能需要缓存每个路径的边界,例如使用并行的NSArrayNSValue或通过将路径和边界包装在一起的对象来自己设计。然后,您只需计算一次边界,以后的绘图运行中仅需检索即可。


您能否给我提供关于NSClipRect的文档指针?谷歌只返回了7个结果,其中这个页面是排名最高的。 - Dan
抱歉,我总是把单词的顺序搞混。应该是NSRectClip。我已经更正了帖子并添加了链接。 - Peter Hosey

2

2
如果您需要绘制许多贝塞尔路径,那么OpenGL可能是最好的选择。苹果公司的文档是一个很好的起点。他们有示例代码,用于在Cocoa窗口中实现基本的OpenGL渲染上下文。
这将把复杂的绘图任务转移到图形处理器上,并使您的应用程序自由滚动。 此页面提供了用于绘制贝塞尔曲线的OpenGL示例代码。

2
我会看CATiledLayer(仅适用于Leopard)来完成这个功能。您可以在瓷砖图层中绘制大量的内容,并根据需要显示区域。对于您的情况,您可以将CATiledLayer设置为NSView的后备,将所有Bezier路径绘制到该图层中,然后在其上滚动甚至放大和缩小。Core Animation图层类似于OpenGL纹理,因此您应该从中获得非常好的性能。矢量绘制被缓存在图层中,并且不像在标准NSView上发现的那样,在滚动时不重新绘制。
例如,Bill Dudney发布了一些示例代码,演示如何使用CATiledLayer来显示大型PDF文件

2
数据集有多动态?
如果数据集相对稳定,并且您想要“缩放”,那么您应该考虑将数据合并成一个经过缩放的子集。
举个例子:如果您有100,000个点(沿X轴前进),那么在一个1000像素的图像中,您只会绘制其中的1000个点。
因此,您可以提前选择适当的数据集(1000个点、10000个点、原始数据100000个点),并根据显示的“缩放”级别从中选择。
至于在点重叠时选择哪个值,您可以选择最小值、最大值、中位数、平均值等。

0

考虑使用一个能够高效处理这种事情的框架,比如CorePlot


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接