在 Android Canvas 中快速绘制大量线条:处理抗锯齿和 KitKat 的特性
Fast drawing lots of lines in Android Canvas: dealing with antialiasing and KitKat's peculiarities
我需要快速抽签(~10k/帧是常见数量)。绘制这些线条的已知方法(单线程,抗锯齿):
drawLines()
硬件加速 Canvas
。速度很快,但线条变得抗锯齿模糊(见下图)。此外,在 KitKat
上,如果点数组包含 NaN
个值 ,则不会绘制任何内容
drawLines()
用软件呈现 Canvas
。速度适中,没有模糊,KitKat
问题仍然存在
for (int i ...) drawLine()
硬件加速 Canvas
。列出的三个中最慢的,没有模糊,没有 KitKat
个问题
有些事情告诉我有一些简单的技巧可以避免抗锯齿和 KitKat
问题以保持高性能
对于第一个 - 例如,是否可以绘制那些未抗锯齿的线条,然后对整个位图应用抗锯齿(最快选项的一种变体)?
对于第二个 - 没有想法,但有一些处理 NaN
s 的微调方法。无论如何,KitKat
是一个相对较新和流行的 - 它的问题应该有一些解决方案 - 否则该平台使用起来会很头疼
UPD2:
这个问题包含两个不同的问题:
- 使用 drawLines()
硬件加速和任意(但有效)输入时的抗锯齿
- drawLines()
如果输入包含 NaN
s
,则拒绝在 KitKat
上绘制任何内容
drawLines()
是重点,因为它 way 比使用 drawLine()
一条一条地画线快 way
另外,下图是对同一个数组应用drawLines()
两次的结果:
canvas.drawLines(toDraw, 0, qty, paint);
canvas.drawLines(toDraw, 2, qty-4, paint); //qty % 4 == 0
UPD:
这就是模糊的样子。它在行的末端
这里的话题有点乱;让我们试着解开它们。
行"blurring"
根据我们的聊天记录,此问题只会在您开始绘制长度 < 1 像素的线段时出现。值得注意的是 drawLines()
将一次细分所有线段,并且由于集合中存在 <1px 线,细分器可能会倒塌。这就是您使用单独的 drawLine()
命令看不到 "blurring" 的原因;每个 drawLine()
仅细分您给它的部分,因此错误仅限于那些超小的部分(无论如何都太小了)。 这里的解决方法 是添加一些逻辑来从你的集合中删除 <1px 长度的线,这将解决这个问题,并允许你使用更快的 drawLines()
比其他方法。
NaN 问题
NaN 会在 GPU 上引起很多问题,因此如果它们包含在您的绘图列表中,您会发现问题是有道理的。 (很像导致模糊问题的 <1px 线段)。同样,这就是为什么您使用单独的 drawLine()
命令看不到视觉问题; NaN 正在破坏 tesselator,并将其仅隔离到那些单个段,而不是整个行列表。同样,此处的解决方案 将指向过滤列表以删除 NaN。
性能
考虑到镶嵌一条线的开销明显大于 CPU 检查以丢弃一条坏线,因此添加一个预处理以删除 NaN 和 <1px 线应该在您的性能预算,并会消除您看到的视觉问题。
好吧,解决方案就是聊天中写的结果(参见 Colt 的 post):
1) 原始输入数组被拆分成不包含 NaN 的片段
2)这些片段被拆分,使得子片段中线长度之间的最大差异小于4x
(实验获得的值)。将 drawLines()
应用于最终的子数组不会产生模糊并且性能相当不错
我需要快速抽签(~10k/帧是常见数量)。绘制这些线条的已知方法(单线程,抗锯齿):
drawLines()
硬件加速Canvas
。速度很快,但线条变得抗锯齿模糊(见下图)。此外,在KitKat
上,如果点数组包含NaN
个值 ,则不会绘制任何内容
drawLines()
用软件呈现Canvas
。速度适中,没有模糊,KitKat
问题仍然存在for (int i ...) drawLine()
硬件加速Canvas
。列出的三个中最慢的,没有模糊,没有KitKat
个问题
有些事情告诉我有一些简单的技巧可以避免抗锯齿和 KitKat
问题以保持高性能
对于第一个 - 例如,是否可以绘制那些未抗锯齿的线条,然后对整个位图应用抗锯齿(最快选项的一种变体)?
对于第二个 - 没有想法,但有一些处理 NaN
s 的微调方法。无论如何,KitKat
是一个相对较新和流行的 - 它的问题应该有一些解决方案 - 否则该平台使用起来会很头疼
UPD2:
这个问题包含两个不同的问题:
- 使用 drawLines()
硬件加速和任意(但有效)输入时的抗锯齿
- drawLines()
如果输入包含 NaN
s
KitKat
上绘制任何内容
drawLines()
是重点,因为它 way 比使用 drawLine()
另外,下图是对同一个数组应用drawLines()
两次的结果:
canvas.drawLines(toDraw, 0, qty, paint);
canvas.drawLines(toDraw, 2, qty-4, paint); //qty % 4 == 0
UPD:
这就是模糊的样子。它在行的末端
这里的话题有点乱;让我们试着解开它们。
行"blurring"
根据我们的聊天记录,此问题只会在您开始绘制长度 < 1 像素的线段时出现。值得注意的是 drawLines()
将一次细分所有线段,并且由于集合中存在 <1px 线,细分器可能会倒塌。这就是您使用单独的 drawLine()
命令看不到 "blurring" 的原因;每个 drawLine()
仅细分您给它的部分,因此错误仅限于那些超小的部分(无论如何都太小了)。 这里的解决方法 是添加一些逻辑来从你的集合中删除 <1px 长度的线,这将解决这个问题,并允许你使用更快的 drawLines()
比其他方法。
NaN 问题
NaN 会在 GPU 上引起很多问题,因此如果它们包含在您的绘图列表中,您会发现问题是有道理的。 (很像导致模糊问题的 <1px 线段)。同样,这就是为什么您使用单独的 drawLine()
命令看不到视觉问题; NaN 正在破坏 tesselator,并将其仅隔离到那些单个段,而不是整个行列表。同样,此处的解决方案 将指向过滤列表以删除 NaN。
性能
考虑到镶嵌一条线的开销明显大于 CPU 检查以丢弃一条坏线,因此添加一个预处理以删除 NaN 和 <1px 线应该在您的性能预算,并会消除您看到的视觉问题。
好吧,解决方案就是聊天中写的结果(参见 Colt 的 post):
1) 原始输入数组被拆分成不包含 NaN 的片段
2)这些片段被拆分,使得子片段中线长度之间的最大差异小于4x
(实验获得的值)。将 drawLines()
应用于最终的子数组不会产生模糊并且性能相当不错