在NSView或其他东西中绘制大量数据?

时间:2009-01-29 20:43:55

标签: objective-c cocoa graphics nsview

我想要向用户表示数十万个时间序列数据点。我目前的解决方案是将所述数据呈现给带有第三方库的PNG,然后将该PNG加载到NSImage并在滚动视图中显示它。这很有效,除了:

      
  1. 超过32k像素宽的NSImages无法正常显示   
  2. 我希望能够快速轻松地放大数据   
  3. 从磁盘读取和写入是愚蠢的

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

我确信,作为一个相对的Cocoa新手,我错过了一些更好的方法来做到这一点。什么是“正确”的方式呢?

6 个答案:

答案 0 :(得分:6)

  

我目前的尝试是直接将NSBezierPaths绘制到NSView。即使我一次只绘制一个有限的点子集,视图渲染得非常漂亮,但非常非常缓慢。每次我滚动我都要重新绘制,这也很慢。

在您采取任何激烈的解决方案之前,请尝试以下更简单的步骤:

  1. 剪辑。使用NSRectClip,将您作为参数的矩形传递给drawRect:。这是一个单行。
  2. 在填充路径之前进行简单的矩形测试。对于每个路径,获取其边界并使用NSIntersectsRect来测试它是否在矩形内。
  3. 在抚摸路径之前进行稍微不那么简单的矩形测试。与上一步类似,除了你需要通过线宽增长矩形(你收到的那个作为参数),因为一半的中风将落在路径之外。 对于每个路径,获取线宽并取消它(delta = -[path lineWidth] - 是的,必须包含该减号),然后将结果作为两个参数传递给NSInsetRect。请确保保留原始矩形,因为不同的路径可能具有不同的线宽。
  4. 这个想法很简单,就是减少吸引力。设置剪切路径(使用NSRectClip)将减少绘制操作导致的blitting数量。排除完全位于绘制矩形之外的路径将为这些路径节省可能更昂贵的剪辑。

    当然,您应该在每个步骤进行分析,以确保您没有放慢速度。您可能想要设置某种帧速率计数器。

    还有一件事:获取每条路径的边界可能很昂贵。 (再次,配置文件。)如果是这样,您可能希望缓存每个路径的边界,可能使用并行的NSArray NSValue或通过将路径和边界包装在您自己的对象中制定。然后,您只计算一次边界,并在将来的绘图运行中仅检索它。

答案 1 :(得分:2)

文档中有NSBezierPath performance部分。

答案 2 :(得分:2)

如果您正在绘制那么多bezier路径,那么OpenGL可能就是您的选择。 Apple的documentation将是一个很好的起点。他们有sample code用于在可可窗口中实现基本的OpenGL渲染上下文。

这会将困难绘图任务的负担转移到图形处理器上,让您的应用程序随意滚动。

This page有用于绘制贝塞尔曲线的OpenGL代码示例。

答案 3 :(得分:2)

我会看看CATiledLayer(仅限Leopard)。您可以在平铺图层中绘制大量内容,并根据需要显示区域。对于您的情况,您可以将CATiledLayer设置为NSView的支持,将所有Bezier路径绘制到该层,然后滚动甚至放大和缩小。核心动画层的处理方式与OpenGL纹理类似,因此您应该从中获得非常好的性能。矢量绘图缓存在图层中,并且在您滚动时不会重绘,就像您在标准NSView上找到的那样。

例如,Bill Dudney has posted some sample code关于如何使用CATiledLayer显示大量PDF文件。

答案 4 :(得分:2)

数据集的动态程度如何?

如果它是相当静态的,并且您想要“缩放”,那么您应该考虑将数据合并到缩放的子集中。

举例说明:如果你有10万分(比如在X轴上前进),那么,显然,在一个1000像素的图像中,你将不可避免地绘制出1000分的1000分。

因此,你可以在高级(1000分,10000分,100000分的原始数据)中做到这一点,并根据你正在显示的“缩放”级别从相应的集合中选择。

当点重叠时选择什么值,你可以做min,max,median,average等。

答案 5 :(得分:0)

考虑使用能够有效处理这类内容的框架,例如CorePlot