【问题标题】:Performance improvements to SVG path rendering in CanvasCanvas 中 SVG 路径渲染的性能改进
【发布时间】:2012-06-11 14:57:20
【问题描述】:

Tl;DR:我的渲染时间出奇地慢,使用下面的代码 - 任何想法为什么会这样或关于如何提高性能的建议将不胜感激。

我正在开发一个需要支持多个渲染后端的应用程序(例如SVGCanvas),但是我遇到了一些在渲染到 Canvas 时没有预料到的严重性能问题。

对,所以我的代码是这样构建的,这样每个后端都实现了一个类似 Canvas 的绘图 API,这样无论使用什么后端,渲染语法都保持不变。我的代码正在渲染定义为 SVG 路径(最初来自 SVG 字体文件)的“字形”。问题是画布渲染速度非常慢 - 几乎与具有 大量 DOM 交互的 SVG 一样慢。

我希望渲染时间允许动画帧速率至少为 30 FPS,但现在,使用 Canvas 时,单个帧大约需要 50-70 毫秒(Chrome),而使用 SVG 时需要 80-90 毫秒。最好的结果是~20 FPS。我现在正在渲染 30 个字形,平均数量约为 24.5 个绘图命令。

我的问题是:有没有更有效的方法来进行这种渲染或任何方法来获得更好的性能,因为我对这种方法的低效率感到震惊(即使我缓存了字形!)。 Glyph 是一个对象,它在初始化时将 SVG 路径字符串解码为(我认为)更快的符号,这使得路径成为数组数组。例如:

[['M', 201, 203.5551],['s', 15.2, 13.254, 15.3, 18.5, 22.3, 50.118], ...]

我的CanvasRenderingContext2D#renderGlyph 方法被定义为类似,glyph.path 对象(数组)的定义如上:

canvas.renderGlyph = function canvasRenderGlyph(name, x, y, nocache) {
  if (!(name instanceof Glyph) && !font.glyphs[name]) {
    return console.log('Unsupported Glyph: ' + name, 'warn');
  }
  x = x * scale;
  y = y * scale;
  var glyph, path, c, startx, starty, px, py, controlpx, controlpy;
  if (typeof name === 'string' && name in glyphCache && !nocache) {
    glyph = glyphCache[name];
  } else {
    glyph = (name instanceof Glyph) ? name : new Glyph(font.glyphs[name]);
    glyph.scale(scale * font.scale.x, scale * font.scale.y);
    if (typeof name === 'string') {
      glyphCache[name] = glyph;
    }
  }
  path = glyph.path;
  startx = x;
  starty = y;
  px = 0;
  py = 0;
  this.beginPath();
  for (var i = 0, length = path.length; i < length; i++) {
    c = path[i];
    switch (c[0]) {
    case 'M':
      px = c[1];
      py = c[2];
      this.moveTo(startx + px, starty + py);
      break;
    case 'l':
      px += c[1];
      py += c[2];
      this.lineTo(startx + px, starty + py);
      break;
    case 'h':
      px += c[1];
      this.lineTo(startx + px, starty + py);
      break;
    case 'v':
      py += c[1];
      this.lineTo(startx + px, starty + py);
      break;
    case 'q':
      controlpx = px + c[1];
      controlpy = py + c[2];
      px += c[3];
      py += c[4];
      this.quadraticCurveTo(
      startx + controlpx, starty + controlpy, startx + px, starty + py);
      break;
    case 't':
      controlpx = px + (px - controlpx);
      controlpy = py + (py - controlpy);
      px += c[1];
      py += c[2];
      this.quadraticCurveTo(
      startx + controlpx, starty + controlpy, startx + px, starty + py);
      break;
    case 'c':
      controlpx = px + c[3];
      controlpy = py + c[4];
      this.bezierCurveTo(
      startx + px + c[1], starty + py + c[2], startx + controlpx, starty + controlpy, startx + px + c[5], starty + py + c[6]);
      px += c[5];
      py += c[6];
      break;
    case 's':
      this.bezierCurveTo(
      startx + controlpx, starty + controlpy, startx + px + c[1], starty + py + c[2], startx + px + c[3], starty + py + c[4]);
      px += c[3];
      py += c[4];
      controlpx = px + c[1];
      controlpy = py + c[2];
      break;
    case 'z':
      this.closePath();
      break;
    default:
      if (c[0].match(/[a-z]/i)) {
        console.log('Unsupported path command: ' + cname, name, 'warn');
      }
      break;
    }
  }
  this.fillStyle = self.settings.fillcolor;
  this.fill();
};

【问题讨论】:

  • 如果你真的直接使用 svgfont 或 truetype webfont 的性能如何?与您的 js 实现相比,只是对此感到好奇。
  • 没试过。然而,我尝试使用ctx.drawImage,它工作得非常好,因为它是异步的,但是它又太慢了,而且不适合动画(因为字形在它们准备好时会呈现,这会导致一个明显的烦人问题)

标签: javascript performance canvas svg


【解决方案1】:

这肯定有问题。性能应该不会太差,渲染大约 24 个命令复杂度的字形。

例如,

Fabric.js 能够使用 thousands 命令以 30fps 的速度渲染路径。在 Fabric 中,我使用类似的方法将 SVG 路径数据解析为命令数组,然后调用相应的上下文方法。

还要考虑 Fabric recognizes more commands(您的示例缺少大多数绝对值 - Q、C、S 等)

在这个animation example 中,您可以看到~4000、~5000 条路径的表现不错。只有当它上升到 ~10000 时,您才会开始看到减速(此时 FPS 计数器已失效,所以我只谈论感知性能)。

其他可能影响性能的因素——画布大小、页面上可能存在的其他元素、动画循环。另外,您在哪个硬件/平台上发现性能不佳?

【讨论】:

  • 感谢您的回答。缺少可识别命令是一个深思熟虑的决定,因为该字体不使用大多数绝对命令。我在 Chrome/Win7 和 Chromium/Arch 上对此进行了测试,它们都在合理的硬件上运行。我不知道画布大小会影响渲染时间,但遗憾的是我不认为这是导致它的原因 - 它大多数在其他代码中。这可能应该被关闭为无效,因为我没有提供足够的细节?
  • 我明白了。好吧,你总是可以在调查后留下另一个答案——如果你发现问题所在。或者修改问题。祝你好运。
猜你喜欢
  • 1970-01-01
  • 2011-04-25
  • 2019-10-29
  • 1970-01-01
  • 2021-11-26
  • 2018-10-03
  • 1970-01-01
  • 2012-03-16
  • 1970-01-01
相关资源
最近更新 更多