在 iOS 上高效绘制包含大量点的图表
Drawing chart with lots of points efficiently on iOS
我正在寻找一种方法来在 iOS 上高效地绘制包含大量点(最多 10 000 个)的折线图。该图获取实时数据,需要每秒更新多次。我正在尝试找出一种有效绘制图形的方法,这样它就不会在绘制时最大化 CPU 并阻塞主线程。
我目前正在为折线图创建一个 UIBezierPath
,在后台线程中,添加所有点并在 CALayer(已启用异步绘图)中绘制它。它不是很快,最大化 CPU 并且绘图太慢 UI 变得迟钝。我正在绘制实时数据,理论上我每次都可以重复使用相同的 UIBezierPath
并仅附加新点,但是一段时间后旧值会被丢弃,这意味着必须删除已删除值的点从贝塞尔路径,这是不可能的。
我还没有在 iOS 上找到任何关于高效绘制大型数据集的信息,但我希望有一些方法可以使用 GPU 来提高性能。
几年前我遇到过这样的情况(回到 iPhone 4 天)。
我使用了一个 CGMutablePathRef
,我使用 CGPathMoveToPoint
添加点作为第一个点,然后使用 CGPathAddLineToPoint
添加点。
为了获得可接受的性能,我将数据点存储在 C 数组中(而不是 NSArray
)。另外,当绘制一个点时,如果它的显示坐标与最后一个绘制点相同,我就跳过了对 CGPathAddLineToPoint
的调用。 (当数据点多于像素时,通常会出现这种情况)。
我不记得确切的绘制时间,但速度出奇地快。
与其使用 UIBezierPath
绘制点,不如设置 CGBitmapContext
,然后绘制点 "by hand",例如
color = (0xFF << 24) | (blue << 16) | (green << 8) | (red); // AABBGGRR
for (loop = 0; loop < count; loop++)
{
index = indices[loop];
self.pixels[index] = color;
}
其中 self.pixels 是您用来创建 CGBitmapContext 的缓冲区。然后,在 drawRect:
CGContextRef c = UIGraphicsGetCurrentContext();
CGImageRef image = CGBitmapContextCreateImage(self.imageContext);
CGContextDrawImage(c, self.bounds, image);
CGImageRelease(image);
我认为您会发现这比使用 CGPath 或 UIBezierPath 快得多(而且更小)。
我终于找到了用 OpenGL 绘制图形的实现:https://github.com/syedhali/EZAudio/blob/master/EZAudio/EZAudioPlotGL.m
其实很简单,而且性能提升很大。
对于配备 A7 芯片(或更高版本)的设备,用金属绘制图形可能会更快,这也应该很容易。有一些关于如何操作的指南:https://www.raywenderlich.com/77488/ios-8-metal-tutorial-swift-getting-started
我正在寻找一种方法来在 iOS 上高效地绘制包含大量点(最多 10 000 个)的折线图。该图获取实时数据,需要每秒更新多次。我正在尝试找出一种有效绘制图形的方法,这样它就不会在绘制时最大化 CPU 并阻塞主线程。
我目前正在为折线图创建一个 UIBezierPath
,在后台线程中,添加所有点并在 CALayer(已启用异步绘图)中绘制它。它不是很快,最大化 CPU 并且绘图太慢 UI 变得迟钝。我正在绘制实时数据,理论上我每次都可以重复使用相同的 UIBezierPath
并仅附加新点,但是一段时间后旧值会被丢弃,这意味着必须删除已删除值的点从贝塞尔路径,这是不可能的。
我还没有在 iOS 上找到任何关于高效绘制大型数据集的信息,但我希望有一些方法可以使用 GPU 来提高性能。
几年前我遇到过这样的情况(回到 iPhone 4 天)。
我使用了一个 CGMutablePathRef
,我使用 CGPathMoveToPoint
添加点作为第一个点,然后使用 CGPathAddLineToPoint
添加点。
为了获得可接受的性能,我将数据点存储在 C 数组中(而不是 NSArray
)。另外,当绘制一个点时,如果它的显示坐标与最后一个绘制点相同,我就跳过了对 CGPathAddLineToPoint
的调用。 (当数据点多于像素时,通常会出现这种情况)。
我不记得确切的绘制时间,但速度出奇地快。
与其使用 UIBezierPath
绘制点,不如设置 CGBitmapContext
,然后绘制点 "by hand",例如
color = (0xFF << 24) | (blue << 16) | (green << 8) | (red); // AABBGGRR
for (loop = 0; loop < count; loop++)
{
index = indices[loop];
self.pixels[index] = color;
}
其中 self.pixels 是您用来创建 CGBitmapContext 的缓冲区。然后,在 drawRect:
CGContextRef c = UIGraphicsGetCurrentContext();
CGImageRef image = CGBitmapContextCreateImage(self.imageContext);
CGContextDrawImage(c, self.bounds, image);
CGImageRelease(image);
我认为您会发现这比使用 CGPath 或 UIBezierPath 快得多(而且更小)。
我终于找到了用 OpenGL 绘制图形的实现:https://github.com/syedhali/EZAudio/blob/master/EZAudio/EZAudioPlotGL.m
其实很简单,而且性能提升很大。
对于配备 A7 芯片(或更高版本)的设备,用金属绘制图形可能会更快,这也应该很容易。有一些关于如何操作的指南:https://www.raywenderlich.com/77488/ios-8-metal-tutorial-swift-getting-started